2012-01-13 130 views
0

我有一個從sdcard獲取位圖的活動。其視圖設置爲自定義視圖組。我已經動態地將包含位圖的視圖添加到視圖組,並使用處理程序更新UI線程中的視圖組,但無濟於事。視圖組不顯示位圖。我沒有編寫自己的視圖組代碼,但應該允許用戶輕掃屏幕以加載不同的視圖/位圖。Viewgroup not drawing its children

我已檢查位圖是否爲空,並將日誌語句放在兩個文件中的各個位置。關於爲什麼視圖組的孩子不被繪製的任何想法?

活動

public class HorizontalPagerActivity extends Activity { 


    private static final String TAG = "*********hpActivity"; 
    private Context mContext = this; 
    File tempFile; 
    byte [] imageArray; 
    private Bitmap b = null; 
    private Handler handler; 

    @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 

      requestWindowFeature(Window.FEATURE_NO_TITLE); 
      getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
        WindowManager.LayoutParams.FLAG_FULLSCREEN); 



      setContentView(R.layout.hpview); 
      final ViewGroup viewgroup = (ViewGroup)findViewById(R.id.hpview); 
      handler = new Handler(); 



      tempFile = new File(Environment.getExternalStorageDirectory(). 
        getAbsolutePath() + "/"+"image.jpeg"); 

      Log.e(TAG, "image length = "+tempFile.length()); 

      imageArray = new byte[(int)tempFile.length()]; 





     try{ 

       InputStream is = new FileInputStream(tempFile); 
       BufferedInputStream bis = new BufferedInputStream(is); 
       DataInputStream dis = new DataInputStream(bis); 


       int i = 0; 

       while (dis.available() > 0) { 
       imageArray[i] = dis.readByte(); 
       i++; 
       } 

       dis.close(); 


      } catch (Exception e) { 

        e.printStackTrace(); 
       } 



      Bitmap bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length); 
      // Bitmap b = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888); 
      b = bm.copy(bm.getConfig(), true); 

      if(b == null){ 
       Log.e(TAG, "b = null"); 
      }else{ 
       Log.e(TAG, "b = not null"); 
      } 


      Canvas canvas = new Canvas(b); 

      Log.e(TAG, "canvas created"); 

      final View view = new View(this); 
      Log.e(TAG, "view created"); 

      // LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); 

      // view.setLayoutParams(lp); 
      view.draw(canvas); 
      viewgroup.addView(view); 

      Log.e(TAG, "view added to viewgroup"); 


      Runnable runnable = new Runnable() { 
       @Override 
       public void run() { 



         handler.post(new Runnable() { 
          @Override 
          public void run() { 

           Log.e(TAG, "about to inval viewgroup"); 
           viewgroup.invalidate(); 
           Log.e(TAG, "finished inval viewgroup"); 

          } 
         }); 

       } 
      }; 
      new Thread(runnable).start(); 

      Log.e(TAG, "finished handler"); 








      /* 
      runOnUiThread(new Runnable() { 

       @Override 
       public void run() { 
        // TODO Auto-generated method stub 
        Log.e(TAG, "about to inval viewgroup"); 
        viewgroup.postInvalidate(); 
        Log.e(TAG, "finished inval viewgroup"); 
       } 
      }); 

      */ 

      Log.e(TAG, "no of chidren = "+viewgroup.getChildCount()); 





    } 


} 

的ViewGroup中

/** 
* A view group that allows users to switch between multiple screens (layouts) in the same way as 
* the Android home screen (Launcher application). 
* <p> 
* You can add and remove views using the normal methods {@link ViewGroup#addView(View)}, 
* {@link ViewGroup#removeView(View)} etc. You may want to listen for updates by calling 
* {@link HorizontalPager#setOnScreenSwitchListener(OnScreenSwitchListener)} in order to perform 
* operations once a new screen has been selected. 
* 
* Modifications from original version (ysamlan): Animate argument in setCurrentScreen and duration 
* in snapToScreen; onInterceptTouchEvent handling to support nesting a vertical Scrollview inside 
* the RealViewSwitcher; allowing snapping to a view even during an ongoing scroll; snap to 
* next/prev view on 25% scroll change; density-independent swipe sensitivity; width-independent 
* pager animation durations on scrolling to properly handle large screens without excessively 
* long animations. 
* 
* Other modifications: 
* (aveyD) Handle orientation changes properly and fully snap to the right position. 
* 
* @author Marc Reichelt, <a href="http://www.marcreichelt.de/">http://www.marcreichelt.de/</a> 
* @version 0.1.0 
*/ 
public final class HorizontalPager extends ViewGroup { 
    /* 
    * How long to animate between screens when programmatically setting with setCurrentScreen using 
    * the animate parameter 
    */ 
    private static final int ANIMATION_SCREEN_SET_DURATION_MILLIS = 500; 
    // What fraction (1/x) of the screen the user must swipe to indicate a page change 
    private static final int FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE = 4; 
    private static final int INVALID_SCREEN = -1; 
    /* 
    * Velocity of a swipe (in density-independent pixels per second) to force a swipe to the 
    * next/previous screen. Adjusted into mDensityAdjustedSnapVelocity on init. 
    */ 
    private static final int SNAP_VELOCITY_DIP_PER_SECOND = 600; 
    // Argument to getVelocity for units to give pixels per second (1 = pixels per millisecond). 
    private static final int VELOCITY_UNIT_PIXELS_PER_SECOND = 1000; 

    private static final int TOUCH_STATE_REST = 0; 
    private static final int TOUCH_STATE_HORIZONTAL_SCROLLING = 1; 
    private static final int TOUCH_STATE_VERTICAL_SCROLLING = -1; 
    private int mCurrentScreen; 
    private int mDensityAdjustedSnapVelocity; 
    private boolean mFirstLayout = true; 
    private float mLastMotionX; 
    private float mLastMotionY; 
    private OnScreenSwitchListener mOnScreenSwitchListener; 
    private int mMaximumVelocity; 
    private int mNextScreen = INVALID_SCREEN; 
    private Scroller mScroller; 
    private int mTouchSlop; 
    private int mTouchState = TOUCH_STATE_REST; 
    private VelocityTracker mVelocityTracker; 
    private int mLastSeenLayoutWidth = -1; 
    private static final String TAG = "*********horizontalpager"; 
    private Bitmap bm = null; 

    /** 
    * Simple constructor to use when creating a view from code. 
    * 
    * @param context The Context the view is running in, through which it can 
    *  access the current theme, resources, etc. 
    */ 
    public HorizontalPager(final Context context) { 
     super(context); 
     Log.e(TAG, "inside hp standard constructor"); 
     init(); 

    } 

    /** 
    * Constructor that is called when inflating a view from XML. This is called 
    * when a view is being constructed from an XML file, supplying attributes 
    * that were specified in the XML file. This version uses a default style of 
    * 0, so the only attribute values applied are those in the Context's Theme 
    * and the given AttributeSet. 
    * 
    * <p> 
    * The method onFinishInflate() will be called after all children have been 
    * added. 
    * 
    * @param context The Context the view is running in, through which it can 
    *  access the current theme, resources, etc. 
    * @param attrs The attributes of the XML tag that is inflating the view. 
    * @see #View(Context, AttributeSet, int) 
    */ 
    public HorizontalPager(final Context context, final AttributeSet attrs) { 
     super(context, attrs); 
     Log.e(TAG, "inside hp constructor for xml inflation"); 
     init(); 


    } 

    /** 
    * Sets up the scroller and touch/fling sensitivity parameters for the pager. 
    */ 
    private void init() { 
     mScroller = new Scroller(getContext()); 

     // Calculate the density-dependent snap velocity in pixels 
     DisplayMetrics displayMetrics = new DisplayMetrics(); 
     ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay() 
       .getMetrics(displayMetrics); 
     mDensityAdjustedSnapVelocity = 
       (int) (displayMetrics.density * SNAP_VELOCITY_DIP_PER_SECOND); 

     final ViewConfiguration configuration = ViewConfiguration.get(getContext()); 
     mTouchSlop = configuration.getScaledTouchSlop(); 
     mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 

    } 

    @Override 
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     final int width = MeasureSpec.getSize(widthMeasureSpec); 
     final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
     if (widthMode != MeasureSpec.EXACTLY) { 
      throw new IllegalStateException("ViewSwitcher can only be used in EXACTLY mode."); 
     } 

     final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
     if (heightMode != MeasureSpec.EXACTLY) { 
      throw new IllegalStateException("ViewSwitcher can only be used in EXACTLY mode."); 
     } 

     // The children are given the same width and height as the workspace 
     final int count = getChildCount(); 
     for (int i = 0; i < count; i++) { 
      getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 
     } 

     if (mFirstLayout) { 
      scrollTo(mCurrentScreen * width, 0); 
      mFirstLayout = false; 
     } 

     else if (width != mLastSeenLayoutWidth) { // Width has changed 
      /* 
      * Recalculate the width and scroll to the right position to be sure we're in the right 
      * place in the event that we had a rotation that didn't result in an activity restart 
      * (code by aveyD). Without this you can end up between two pages after a rotation. 
      */ 
      Display display = 
        ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)) 
          .getDefaultDisplay(); 
      int displayWidth = display.getWidth(); 

      mNextScreen = Math.max(0, Math.min(getCurrentScreen(), getChildCount() - 1)); 
      final int newX = mNextScreen * displayWidth; 
      final int delta = newX - getScrollX(); 

      mScroller.startScroll(getScrollX(), 0, delta, 0, 0); 
     } 

     mLastSeenLayoutWidth = width; 
    } 

    @Override 
    protected void onLayout(final boolean changed, final int l, final int t, final int r, 
      final int b) { 
     int childLeft = 0; 
     final int count = getChildCount(); 

     for (int i = 0; i < count; i++) { 
      final View child = getChildAt(i); 
      if (child.getVisibility() != View.GONE) { 
       final int childWidth = child.getMeasuredWidth(); 
       child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); 
       childLeft += childWidth; 
      } 
     } 
    } 

    @Override 
    public boolean onInterceptTouchEvent(final MotionEvent ev) { 
     /* 
     * By Yoni Samlan: Modified onInterceptTouchEvent based on standard ScrollView's 
     * onIntercept. The logic is designed to support a nested vertically scrolling view inside 
     * this one; once a scroll registers for X-wise scrolling, handle it in this view and don't 
     * let the children, but once a scroll registers for y-wise scrolling, let the children 
     * handle it exclusively. 
     */ 
     final int action = ev.getAction(); 
     boolean intercept = false; 

     switch (action) { 
      case MotionEvent.ACTION_MOVE: 
       /* 
       * If we're in a horizontal scroll event, take it (intercept further events). But if 
       * we're mid-vertical-scroll, don't even try; let the children deal with it. If we 
       * haven't found a scroll event yet, check for one. 
       */ 
       if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) { 
        /* 
        * We've already started a horizontal scroll; set intercept to true so we can 
        * take the remainder of all touch events in onTouchEvent. 
        */ 
        intercept = true; 
       } else if (mTouchState == TOUCH_STATE_VERTICAL_SCROLLING) { 
        // Let children handle the events for the duration of the scroll event. 
        intercept = false; 
       } else { // We haven't picked up a scroll event yet; check for one. 

        /* 
        * If we detected a horizontal scroll event, start stealing touch events (mark 
        * as scrolling). Otherwise, see if we had a vertical scroll event -- if so, let 
        * the children handle it and don't look to intercept again until the motion is 
        * done. 
        */ 

        final float x = ev.getX(); 
        final int xDiff = (int) Math.abs(x - mLastMotionX); 
        boolean xMoved = xDiff > mTouchSlop; 

        if (xMoved) { 
         // Scroll if the user moved far enough along the X axis 
         mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING; 
         mLastMotionX = x; 
        } 

        final float y = ev.getY(); 
        final int yDiff = (int) Math.abs(y - mLastMotionY); 
        boolean yMoved = yDiff > mTouchSlop; 

        if (yMoved) { 
         mTouchState = TOUCH_STATE_VERTICAL_SCROLLING; 
        } 
       } 

       break; 
      case MotionEvent.ACTION_CANCEL: 
      case MotionEvent.ACTION_UP: 
       // Release the drag. 
       mTouchState = TOUCH_STATE_REST; 
       break; 
      case MotionEvent.ACTION_DOWN: 
       /* 
       * No motion yet, but register the coordinates so we can check for intercept at the 
       * next MOVE event. 
       */ 
       mLastMotionY = ev.getY(); 
       mLastMotionX = ev.getX(); 
       break; 
      default: 
       break; 
      } 

     return intercept; 
    } 

    @Override 
    public boolean onTouchEvent(final MotionEvent ev) { 
     Log.e(TAG, "inside hp ontouchEvent"); 

     if (mVelocityTracker == null) { 
      mVelocityTracker = VelocityTracker.obtain(); 
     } 
     mVelocityTracker.addMovement(ev); 

     final int action = ev.getAction(); 
     final float x = ev.getX(); 

     switch (action) { 
      case MotionEvent.ACTION_DOWN: 
       /* 
       * If being flinged and user touches, stop the fling. isFinished will be false if 
       * being flinged. 
       */ 
       if (!mScroller.isFinished()) { 
        mScroller.abortAnimation(); 
       } 

       // Remember where the motion event started 
       mLastMotionX = x; 

       if (mScroller.isFinished()) { 
        mTouchState = TOUCH_STATE_REST; 
       } else { 
        mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING; 
       } 

       break; 
      case MotionEvent.ACTION_MOVE: 
       final int xDiff = (int) Math.abs(x - mLastMotionX); 
       boolean xMoved = xDiff > mTouchSlop; 

       if (xMoved) { 
        // Scroll if the user moved far enough along the X axis 
        mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING; 
       } 

       if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) { 
        // Scroll to follow the motion event 
        final int deltaX = (int) (mLastMotionX - x); 
        mLastMotionX = x; 
        final int scrollX = getScrollX(); 

        if (deltaX < 0) { 
         if (scrollX > 0) { 
          scrollBy(Math.max(-scrollX, deltaX), 0); 
         } 
        } else if (deltaX > 0) { 
         final int availableToScroll = 
           getChildAt(getChildCount() - 1).getRight() - scrollX - getWidth(); 

         if (availableToScroll > 0) { 
          scrollBy(Math.min(availableToScroll, deltaX), 0); 
         } 
        } 
       } 

       break; 

      case MotionEvent.ACTION_UP: 
       if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) { 
        final VelocityTracker velocityTracker = mVelocityTracker; 
        velocityTracker.computeCurrentVelocity(VELOCITY_UNIT_PIXELS_PER_SECOND, 
          mMaximumVelocity); 
        int velocityX = (int) velocityTracker.getXVelocity(); 

        if (velocityX > mDensityAdjustedSnapVelocity && mCurrentScreen > 0) { 
         // Fling hard enough to move left 
         snapToScreen(mCurrentScreen - 1); 
        } else if (velocityX < -mDensityAdjustedSnapVelocity 
          && mCurrentScreen < getChildCount() - 1) { 
         // Fling hard enough to move right 
         snapToScreen(mCurrentScreen + 1); 
        } else { 
         snapToDestination(); 
        } 

        if (mVelocityTracker != null) { 
         mVelocityTracker.recycle(); 
         mVelocityTracker = null; 
        } 
       } 

       mTouchState = TOUCH_STATE_REST; 

       break; 
      case MotionEvent.ACTION_CANCEL: 
       mTouchState = TOUCH_STATE_REST; 
       break; 
      default: 
       break; 
     } 

     return true; 
    } 

    @Override 
    public void computeScroll() { 
     if (mScroller.computeScrollOffset()) { 
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 
      postInvalidate(); 
     } else if (mNextScreen != INVALID_SCREEN) { 
      mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); 

      // Notify observer about screen change 
      if (mOnScreenSwitchListener != null) { 
       mOnScreenSwitchListener.onScreenSwitched(mCurrentScreen); 
      } 

      mNextScreen = INVALID_SCREEN; 
     } 
    } 

    /** 
    * Returns the index of the currently displayed screen. 
    * 
    * @return The index of the currently displayed screen. 
    */ 
    public int getCurrentScreen() { 
     return mCurrentScreen; 
    } 

    /** 
    * Sets the current screen. 
    * 
    * @param currentScreen The new screen. 
    * @param animate True to smoothly scroll to the screen, false to snap instantly 
    */ 
    public void setCurrentScreen(final int currentScreen, final boolean animate) { 
     mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); 
     if (animate) { 
      snapToScreen(currentScreen, ANIMATION_SCREEN_SET_DURATION_MILLIS); 
     } else { 
      scrollTo(mCurrentScreen * getWidth(), 0); 
     } 
     invalidate(); 
    } 

    /** 
    * Sets the {@link OnScreenSwitchListener}. 
    * 
    * @param onScreenSwitchListener The listener for switch events. 
    */ 
    public void setOnScreenSwitchListener(final OnScreenSwitchListener onScreenSwitchListener) { 
     mOnScreenSwitchListener = onScreenSwitchListener; 
    } 

    /** 
    * Snaps to the screen we think the user wants (the current screen for very small movements; the 
    * next/prev screen for bigger movements). 
    */ 
    private void snapToDestination() { 
     final int screenWidth = getWidth(); 
     int scrollX = getScrollX(); 
     int whichScreen = mCurrentScreen; 
     int deltaX = scrollX - (screenWidth * mCurrentScreen); 

     // Check if they want to go to the prev. screen 
     if ((deltaX < 0) && mCurrentScreen != 0 
       && ((screenWidth/FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < -deltaX)) { 
      whichScreen--; 
      // Check if they want to go to the next screen 
     } else if ((deltaX > 0) && (mCurrentScreen + 1 != getChildCount()) 
       && ((screenWidth/FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < deltaX)) { 
      whichScreen++; 
     } 

     snapToScreen(whichScreen); 
    } 

    /** 
    * Snap to a specific screen, animating automatically for a duration proportional to the 
    * distance left to scroll. 
    * 
    * @param whichScreen Screen to snap to 
    */ 
    private void snapToScreen(final int whichScreen) { 
     snapToScreen(whichScreen, -1); 
    } 

    /** 
    * Snaps to a specific screen, animating for a specific amount of time to get there. 
    * 
    * @param whichScreen Screen to snap to 
    * @param duration -1 to automatically time it based on scroll distance; a positive number to 
    *   make the scroll take an exact duration. 
    */ 
    private void snapToScreen(final int whichScreen, final int duration) { 
     /* 
     * Modified by Yoni Samlan: Allow new snapping even during an ongoing scroll animation. This 
     * is intended to make HorizontalPager work as expected when used in conjunction with a 
     * RadioGroup used as "tabbed" controls. Also, make the animation take a percentage of our 
     * normal animation time, depending how far they've already scrolled. 
     */ 
     mNextScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); 
     final int newX = mNextScreen * getWidth(); 
     final int delta = newX - getScrollX(); 

     if (duration < 0) { 
      // E.g. if they've scrolled 80% of the way, only animation for 20% of the duration 
      mScroller.startScroll(getScrollX(), 0, delta, 0, (int) (Math.abs(delta) 
        /(float) getWidth() * ANIMATION_SCREEN_SET_DURATION_MILLIS)); 
     } else { 
      mScroller.startScroll(getScrollX(), 0, delta, 0, duration); 
     } 
     Log.e(TAG, "about to call inval****************** on viewgroup"); 
     invalidate(); 

    } 

    /** 
    * Listener for the event that the HorizontalPager switches to a new view. 
    */ 
    public static interface OnScreenSwitchListener { 
     /** 
     * Notifies listeners about the new screen. Runs after the animation completed. 
     * 
     * @param screen The new screen index. 
     */ 
     void onScreenSwitched(int screen); 
    } 

    @Override 
    public void onDraw(Canvas canvas){ 
     super.onDraw(canvas); 
     Log.e(TAG, "inside hp ondraw()"); 

    } 

輸出:

01-13 14:52:00.290: ERROR/*********horizontalpager(9206): inside hp constructor for xml inflation 
01-13 14:52:00.295: ERROR/*********hpActivity(9206): image length = 13215 
01-13 14:52:00.295: INFO/global(9206): Default buffer size used in BufferedInputStream constructor. It would be better to be explicit if an 8k buffer is required. 
01-13 14:52:00.730: DEBUG/dalvikvm(9206): GC freed 353 objects/40040 bytes in 58ms 
01-13 14:52:00.740: ERROR/*********hpActivity(9206): b = not null 
01-13 14:52:00.740: ERROR/*********hpActivity(9206): canvas created 
01-13 14:52:00.740: ERROR/*********hpActivity(9206): view created 
01-13 14:52:00.740: ERROR/*********hpActivity(9206): view added to viewgroup 
01-13 14:52:00.740: ERROR/*********hpActivity(9206): finished handler 
01-13 14:52:00.740: ERROR/*********hpActivity(9206): no of chidren = 1 
01-13 14:52:00.775: ERROR/*********hpActivity(9206): about to inval viewgroup 
01-13 14:52:00.775: ERROR/*********hpActivity(9206): finished inval viewgroup 

回答

3
  1. 失去了畫布,除非你打算吸引到你不需要它的圖像。
  2. 您的圖片正在載入代碼是可怕的。改爲使用BitmapFactory.decodeFile(..)
  3. Bitmap對象從BitmapFactory.decodeFile(..)得到應該去一個ImageView內,一個View

    File file = new File(Environment.getExternalStorageDirectory(), "image.jpeg"); 
    if (file.exists() && file.canRead()) { 
        Bitmap bitmap = BitmapFactory.decodeFile(file.toString()); 
        if (bitmap != null) { 
         ImageView view = new ImageView(this); 
         view.setImageBitmap(bitmap); 
         viewgroup.addView(view); 
        } 
    } 
    
+0

非常感謝!我只是用一個imageview來放置位圖,它和我的原始代碼一起工作。我現在可以看到可怕代碼的含義。我現在就改變它。再次感謝 – turtleboy 2012-01-13 15:47:56