2013-06-20 31 views
11

Giống như trong LinkedIn ba màn hình đầu tiênLàm thế nào để có một hình ảnh rộng hơn di chuyển trong nền

  1. Splash
  2. Buttons
  3. Đăng nhập/đăng ký
  4. Đăng nhập/đăng ký Mẫu

tất cả đều có cùng hình ảnh làm nền, nhưng khi chúng ta di chuyển từ hoạt động này sang hoạt động khác, hình nền sẽ cuộn sang trái từ đúng.

Tôi chỉ có thể thử với overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); Nhưng đó không phải là hình thức của nó.

enter image description here

enter image description here

enter image description here

+0

để bạn muốn một thanh trượt như hoạt ảnh từ trái sang phải? – jigar

Trả lời

25

Điều này được gọi là cuộn thị sai, và tôi đã triển khai nó bằng cách sử dụng 2 lớp: Một cho nội dung và một lớp khác cho nền. Nội dung, bạn đặt nó trên một ViewPager không có nền. Lưu ý rằng thay vì các hoạt động, bạn sẽ sử dụng Fragments (mỗi trang sẽ là một đoạn) sẽ được tạo bởi viewpager. (Xem FragmentStatePagerAdapter)

Nền nằm trên một lớp nền, rõ ràng là phía sau người xem và độc lập với nó. Nó có thể là một hình ảnh bên trong một scrollview, hoặc một hình ảnh mà clipping khu vực bạn sẽ được di chuyển, hoặc một hình ảnh mà bạn render thông qua drawBitmap (x, y). Xin vui lòng xem mã nguồn kèm theo cho giải pháp của tôi, mà kéo dài một Xem mà nền có thể được cuộn chỉ gọi một phương thức "setPercent"

Sau đó, bạn ghi đè

viewPager.setOnPageChangeListener(new OnPageChangeListener(){ 

    @Override 
    public void onPageScrolled(int position, float percent, int pixoffset) { 

     // this is called while user's flinging with: 
     // position is the page number 
     // percent is the percentage scrolled (0...1) 
     // pixoffset is the pixel offset related to that percentage 
     // so we got everything we need .... 

     int totalpages=mViewPagerAdapter.getCount(); // the total number of pages 
     float finalPercentage=((position+percent)*100/totalpages); // percentage of this page+offset respect the total pages 
     setBackgroundX ((int)finalPercentage); 
    } 
} 

void setBackgroundX(int scrollPosition) { 
     // now you have to scroll the background layer to this position. You can either adjust the clipping or 
     // the background X coordinate, or a scroll position if you use an image inside an scrollview ... 
        // I personally like to extend View and draw a scaled bitmap with a clipping region (drawBitmap with Rect parameters), so just modifying the X position then calling invalidate will do. See attached source ParallaxBackground 
      parallaxBackground.setPercent(position); 
} 

Và bây giờ quan điểm nền sai, mà đi đằng sau ViewPager . Tôi đăng ở đây một phiên bản làm việc đầy đủ của ParallaxBackgroundView của riêng tôi. Đây thực sự là mã được thử nghiệm.

 package com.regaliz.gui.views; 

    import android.content.Context; 
    import android.graphics.Bitmap; 
    import android.graphics.Bitmap.Config; 
    import android.graphics.Canvas; 
    import android.graphics.Paint; 
    import android.graphics.Rect; 
    import android.graphics.drawable.BitmapDrawable; 
    import android.graphics.drawable.Drawable; 
    import android.util.AttributeSet; 
    import android.util.Log; 
    import android.view.View; 

    /** 
    * Implements a horizontal parallax background. The image is set via setImageDrawable(), it is then scaled to 150% and 
    * you set the percentage via setPErcentage. 
    * @author rodo 
    */ 

    public class ParallaxBackground extends View { 

     private final static String TAG="ParallaxBackground"; 
     private final static int MODE_PRESCALE=0, MODE_POSTSCALE=1; 

     /** How much a image will be scaled */ 
     /** Warning: A full screen image on a Samsung 10.1 scaled to 1.5 consumes 6Mb !! So be careful */ 
     private final static float FACTOR=1.5f; 

     /** The current background */ 
     private Bitmap mCurrentBackground=null; 

     /** Current progress 0...100 */ 
     private float mOffsetPercent=0; 

     /** Flag to activate */ 
     private boolean isParallax=true; 

     /** The parallax mode (MODE_XXX) */ 
     private int mParallaxMode=MODE_PRESCALE; 

     /** precalc stuff to tighten onDraw calls */ 
     private int mCurrentFactorWidth; 
     private float mCurrentFactorMultiplier; 
     private Rect mRectDestination, mRectSource; 

     private Paint mPaint; 


     public ParallaxBackground(Context context, AttributeSet attrs) { 
      super(context, attrs); 
      construct(context); 
     } 

     public ParallaxBackground(Context context) { 
      super(context); 
      construct(context); 
     } 

     /** 
     * Enables or disables parallax mode 
     * @param status 
     */ 

     public void setParallax(boolean status) { 
      Log.d(TAG, "*** PARALLAX: "+status); 
      isParallax=status; 
     } 

     /** 
     * Sets the parallax memory mode. MODE_PRESCALE uses more memory but scrolls slightly smoother. MODE_POSTSCALE uses less memory but is more CPU-intensive. 
     * @param mode 
     */ 

     public void setParallaxMemoryMode(int mode) { 
      mParallaxMode=mode; 
      if (mCurrentBackground!=null) { 
       mCurrentBackground.recycle(); 
       mCurrentBackground=null; 
      } 
     } 

     /** 
     * Seth the percentage of the parallax scroll. 0 Means totally left, 100 means totally right. 
     * @param percentage The perc, 
     */ 

     public void setPercent(float percentage) { 
      if (percentage==mOffsetPercent) return; 
      if (percentage>100) percentage=100; 
      if (percentage<0) percentage=0; 
      mOffsetPercent=percentage; 
      invalidate(); 
     } 

     /** 
     * Wether PArallax is active or not. 
     * @return ditto. 
     */ 

     public boolean isParallax() { 
      return isParallax && (mCurrentBackground!=null); 
     } 

     /** 
     * We override setBackgroundDrawable so we can set the background image as usual, like in a normal view. 
     * If parallax is active, it will create the scaled bitmap that we use on onDraw(). If parallax is not 
     * active, it will divert to super.setBackgroundDrawable() to draw the background normally. 
     * If it is called with anything than a BitMapDrawable, it will clear the stored background and call super() 
     */ 

     @Override 
     public void setBackgroundDrawable (Drawable d) { 

      Log.d(TAG, "*** Set background has been called !!"); 

      if ((!isParallax) || (!(d instanceof BitmapDrawable))) { 
       Log.d(TAG, "No parallax is active: Setting background normally."); 
       if (mCurrentBackground!=null) { 
        mCurrentBackground.recycle(); // arguably here 
        mCurrentBackground=null; 
       } 
       super.setBackgroundDrawable(d); 
       return; 
      } 

      switch (mParallaxMode) { 

      case MODE_POSTSCALE: 
       setBackgroundDrawable_postscale(d); 
       break; 

      case MODE_PRESCALE: 
       setBackgroundDrawable_prescale(d); 
       break; 
      } 

     } 

     private void setBackgroundDrawable_prescale(Drawable incomingImage) { 

      Bitmap original=((BitmapDrawable) incomingImage).getBitmap(); 
      Log.v(TAG, "Created bitmap for background : original: "+original.getByteCount()+", w="+original.getWidth()+", h="+original.getHeight()); 

      mCurrentBackground=Bitmap.createBitmap((int) (this.getWidth()*FACTOR), this.getHeight(), Config.ARGB_8888); 
      Canvas canvas=new Canvas(mCurrentBackground); 

      // we crop the original image up and down, as it has been expanded to FACTOR 
      // you can play with the Adjustement value to crop top, center or bottom. 
      // I only use center so its hardcoded. 

      float scaledBitmapFinalHeight=original.getHeight()*mCurrentBackground.getWidth()/original.getWidth(); 
      int adjustment=0; 

      if (scaledBitmapFinalHeight>mCurrentBackground.getHeight()) { 
       // as expected, we have to crop up&down to maintain aspect ratio 
       adjustment=(int)(scaledBitmapFinalHeight-mCurrentBackground.getHeight())/4; 
      } 

      Rect srect=new Rect(0,adjustment,original.getWidth(), original.getHeight()-adjustment); 
      Rect drect=new Rect(0,0,mCurrentBackground.getWidth(), mCurrentBackground.getHeight()); 

      canvas.drawBitmap(original, srect, drect, mPaint); 

      Log.v(TAG, "Created bitmap for background : Size: "+mCurrentBackground.getByteCount()+", w="+mCurrentBackground.getWidth()+", h="+mCurrentBackground.getHeight()); 

      // precalc factor multiplier 
      mCurrentFactorMultiplier=(FACTOR-1)*getWidth()/100; 

      original.recycle(); 
      System.gc(); 

      invalidate(); 
     } 



     private void setBackgroundDrawable_postscale (Drawable d) { 

      mCurrentBackground=((BitmapDrawable) d).getBitmap(); 

      int currentBackgroundWidth=mCurrentBackground.getWidth(), 
       currentBackgroundHeight=mCurrentBackground.getHeight(), 
       currentFactorHeight=(int) (currentBackgroundHeight/FACTOR); 

      mCurrentFactorWidth=(int) (currentBackgroundWidth/FACTOR); 
      mCurrentFactorMultiplier=(FACTOR-1)*currentBackgroundWidth/100; 
      mRectDestination=new Rect(0,0,getWidth(), getHeight()); 
      mRectSource=new Rect(0,0,mCurrentFactorWidth,currentFactorHeight); 
      invalidate(); 
     } 

     @Override 
     public void onDraw(Canvas canvas) { 
      if ((isParallax) && (mCurrentBackground!=null)) { 
       if (mParallaxMode==MODE_POSTSCALE) onDraw_postscale(canvas); else onDraw_prescale(canvas); 
      } else super.onDraw(canvas); 
     } 

     private void onDraw_prescale(Canvas canvas) { 
      int oxb=(int) (mCurrentFactorMultiplier*mOffsetPercent); 
      canvas.drawBitmap(mCurrentBackground, -oxb, 0, mPaint); 
     } 

     private void onDraw_postscale(Canvas canvas) { 
      int oxb=(int) (mCurrentFactorMultiplier*mOffsetPercent); 
      mRectSource.left=oxb; 
      mRectSource.right=mCurrentFactorWidth+oxb; 
      canvas.drawBitmap(mCurrentBackground,mRectSource,mRectDestination, mPaint); 
     } 

     private void construct(Context context) { 
      mPaint=new Paint(); 
     } 
    } 

    //// EOF ParallaxBackground.java 

Note: Bạn có thể nhanh chóng ParallaxBackground hoặc lập trình hoặc trong XML. Chỉ cần chắc chắn rằng nó nằm phía sau viewpager. Để dụ nó trong một XML bạn không cần phải làm những điều đặc biệt:

<com.regaliz.gui.views.ParallaxBackground 
    android:id="@+id/masterBackground" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    /> 

Sau đó, bạn có thể sử dụng các thành phần giống như bất kỳ quan điểm khác

ParallaxBackground back=findViewById(R.id.masterBackground); 
back.setBackgroundDrawable(R.drawable.your_cool_drawable); 

Note 2: Nếu bạn đang sử dụng Jelly Bean API, bạn sẽ thấy rằng SetBackgroundDrawable (Drawable d) đã được thay thế bởi setBackground (Drawable d). Tôi chưa sử dụng JB api, nhưng tất cả những gì bạn phải làm là đổi tên setBackgroundDrawable thành setBackground.** Điều này quan trọng **

Lưu ý 3: ParallaxBackgroundView có 2 chế độ: MODE_PRESCALE và MODE_POSTSCALE. Chế độ PRESCALE quy mô một bitmap và giữ nó luôn trong bộ nhớ, do đó onDraw sẽ nhanh hơn. Chế độ POSTSCALE không thực hiện bất kỳ phép tính trước nào, thay vào đó, việc chia tỷ lệ được thực hiện trên onDraw(). Điều này là khá chậm, nhưng nó có thể được sử dụng cho các thiết bị bộ nhớ thấp mà không thể đủ khả năng để giữ một bitmap rất lớn trong bộ nhớ.

Hy vọng điều đó sẽ hữu ích!

Bằng cách này, tôi luôn quan tâm đến việc tối ưu hóa mã của mình, vì vậy nếu ai đó có đề xuất tuyệt vời, đặc biệt hiệu suất hoặc bộ nhớ liên quan, hoặc tăng cường lớp học này, hãy đăng nó !!!

+0

Nếu tôi sử dụng một viewpager tôi không thể thêm chúng vào backstack..the hành vi nút quay lại và cũng là quá trình chuyển đổi mảnh. Không có cách nào tôi có thể sử dụng các hoạt động ở đây? – user1537779

+0

Hoặc là có thể sử dụng một framelaout và có thay thế fragment? mà có thể giải quyết vấn đề backstack của tôi. – user1537779

+0

Các hoạt động luôn có nền tảng riêng của họ, bạn sẽ không thể sử dụng nền và sau đó hoạt động ở trên cùng. FrameLayout + Thay thế phân đoạn? chắc chắn rồi! Bạn cũng có thể sử dụng một ViewFlipper không có mảnh vỡ. Điều với ViewPager + Fragments là nó làm cho việc thiết kế/thêm nhiều mảnh/etc ... cộng với bạn có thể chèn các chuyển tiếp trang đẹp, vv ... vào một thành phần đã được thử nghiệm và thử nghiệm bởi Google. Tất nhiên bạn luôn có thể tự làm điều đó một cách thủ công. BTW- Bạn cũng có thể nắm bắt phím BACK và tự xử lý nó. Nếu tôi là bạn tôi sẽ thử điều này trong khi sử dụng ViewPager, đó là một thành phần đẹp. – rupps

7

Một cách để làm điều đó là để mở rộng ViewPager. Nó đã được thực hiện bởi một ai đó và bạn có thể kiểm tra mã trên github.

+0

Tôi đồng ý, đây là một thành phần tuyệt vời! –

+0

Công trình này tuyệt vời! –

Các vấn đề liên quan