Gallery代码分析及纵向Gallery实现方法

        近期在做项目中需要浏览电影海报,图片浏览使用Android Gallery控件可以很方便实现的。但Android 原生Gallery是不支持纵向翻动。网络上也没找到好控件来实现这样的效果,只好自己把Gallery 的代码拔出来读一编,搞懂实现原理后重写了一个 Vertical Gallery 控件。本文将分享Android Gallery 实现原理,并给出重写纵向gallery的方法。
        要重写出纵向Gallery,需要搞清楚两个方面:1 .Gallery是如何初始Layout的 ; 2. 按键或触屏操作是如何驱动图片滑动的。本文也是从这两个方面来介绍Gallery。
        1. 原生Gallery Layout 实现。
         
                                          图1,Gallery 显示效果
       图1 为假定要gallery 要显示的效果,gallery 的每个item为一张图片,其他Image 1放大图片表示选中焦点项。下面通过序列图来标示其layout的过程。
       第一步:绘制第一张图
      
      第二步:将第一步绘制的图片移动至gallery 中心位置
      
       第三步:绘制右边图片
      
        第四步:绘制左边图片
      
       第五步:设置选择焦点项,此处选中图片放大不是有gallery来实现的,而是有使用gallery时监听选中回调来实现的放大
      
        代码分析:
        
  void layout(int delta, boolean animate) {
          ... ...

        // All views go in recycler while we are in layout
        recycleAllViews();
        // Clear out old views
        //removeAllViewsInLayout();
        detachAllViewsFromParent();
        /*
         * These will be used to give initial positions to views entering the
         * gallery as we scroll
         */
        mRightMost = 0;
        mLeftMost = 0;
        // Make selected view and center it
       
        /*
         * mFirstPosition will be decreased as we add views to the left later
         * on. The 0 for x will be offset in a couple lines down.
         */
        //在设置Adapter 时 初始 selectposition 为0 
        mFirstPosition = mSelectedPosition;
       // 绘制第一张图
        View sel = makeAndAddView(mSelectedPosition, 0, 0, true);
       
        // Put the selected child in the center
        // 通过gallery 中心计算 将选中图片移动只gallery中心需要偏移的位置
        int selectedOffset = childrenLeft + (childrenWidth / 2) - (sel.getWidth() / 2) +
                mSelectedCenterOffset;
        sel.offsetLeftAndRight(selectedOffset);
       // 绘制中心右边的图片
        fillToGalleryRight();
        // 绘制中心左边的图片
        fillToGalleryLeft();
       
        // Flush any cached views that did not get reused above
        mRecycler.clear();
        invalidate();
        // 设置通知选择选中焦点
        checkSelectionChanged();
        mDataChanged = false;
        mNeedSync = false;
        setNextSelectedPositionInt(mSelectedPosition);
       
        updateSelectedItemMetadata();
    }
这个函数定义在文件frameworks/base/core/java/android/widget/Gallery.java中。
      
        makeAndAddView 函数实现获取一个view 并将其设置到指定位置上去,获取View方式有两种,要么从已存在的recycler里获取或者从adapter里获取一个新的View。

        结合以上描述,要实现纵向Gallery,只要根据上面的流程将左换成上,将右换下来执行相关流程便可以完成纵向Gallery的layout。但需要注意的是makeAndAddView的时候会调用setUpChild方法需要计算子view的大小和位置,在计算同样需要将左换上,右换下来计算纵向Gallery 子View 的大小及位置。

     2. Gallery 子View的移动
      有两种方式触发Gallery 图片的移动。1,是通过keydown事件来触发;2,是通过触屏手势来移动图片。Gallery 子View的移动借助于Scroller,Scroller查看Android 动画章节中的介绍( Android 动画原理)。由于keydown事件触发的移动与手势触发的Gallery子View移动中的移动原理相似,这里只介绍按键事件触发的移动,如下图所示。
      
                                             图2:  Gallery 按键移动流程
      第1步:Gallery 接收到左右键按键,往前或往后移动一个子View
      第3步:计算移动一个子View 需要移动的距离
      第4步:设置FlingRunnable 来移动指定距离
      第5步:FlingRunnable通过Scroller的startScroll来触发移动,这里只是设置移动的距离,设置移动时间,并计算出移动的终点位置。
      第6、7步就是移动过程,trackMotionScroll一次移动一定的距离,该距离有Scroller来计算。通过不停的查看是否还要移动,最终移动到目的地。more就是通过scroller来计算是否还需要继续移动,Scroller会根据指定的移动时间及移动距离来计算,这是动画的一个原理,如果性能越好移动的越平滑。如果more为false 表示不再移动,通过endFling来结束,在结束移动的过程中还需要保证移动的目标子View移动到Gallery的中心位置。
从中可以看出,Gallery 通过trackMotionScroll来不停的移动子view于达到目的地。trackMotionScroll代码如下:
     
 void trackMotionScroll(int deltaX) {
        if (getChildCount() == 0) {
            return;
        }

        // 判断移动方向
        boolean toLeft = deltaX < 0;
       
        int limitedDeltaX = getLimitedMotionScrollAmount(toLeft, deltaX);
        if (limitedDeltaX != deltaX) {
            // The above call returned a limited amount, so stop any scrolls/flings
            mFlingRunnable.endFling(false);
            onFinishedMovement();
        }

        // 将所有子View 都移动 limiterDeltaX 的距离
        offsetChildrenLeftAndRight(limitedDeltaX);
        // 去掉移出屏幕的子View
        detachOffScreenChildren(toLeft);
        // 补充移动后空余位置的视图
        if (toLeft) {
            // If moved left, there will be empty space on the right
            fillToGalleryRight();
        } else {
            // Similarly, empty space on the left
            fillToGalleryLeft();
        }
       
        // Clear unused views
        mRecycler.clear();

        // 移动过程中会重新修改选中项
        setSelectionToCenterChild();
        final View selChild = mSelectedChild;
        if (selChild != null) {
            final int childLeft = selChild.getLeft();
            final int childCenter = selChild.getWidth() / 2;
            final int galleryCenter = getWidth() / 2;
            mSelectedCenterOffset = childLeft + childCenter - galleryCenter;
        }
        onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.

        invalidate();
    }
这个函数定义在文件frameworks/base/core/java/android/widget/Gallery.java中。
      通过以上分析,我们已知Gallery 子View移动的实现原理,触屏滑动效果原理相似。要实现纵向Gallery也就不难了,还是那句话,移动及计算位置时左换上,右换下即可。





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package e gallery; import android app Activity; import android os Bundle; import android view LayoutInflater; import android view View; import android view ViewGroup; import android view ViewGroup MarginLayoutParams; import android widget AdapterView; import android widget AdapterView OnItemSelectedListener; import android widget BaseAdapter; import android widget Gallery; import android widget ImageView; import e gallery R; public class MainActivity extends Activity implements OnItemSelectedListener { Gallery gallery; ImageView main imageView; ImageView picture imageView; Gesture+ gesture; int[] ids1 { R drawable b R drawable c R drawable d R drawable f R drawable g }; @Override protected void onCreate Bundle savedInstanceState { super onCreate savedInstanceState ; setContentView R layout main ; main imageView ImageView findViewById R id main imageView ; gallery Gallery findViewById R id gallery ; gallery setOnItemSelectedListener this ; 怎么设置从中间开始 gallery setAdapter new MyAdapter ; } class MyAdapter extends BaseAdapter{ @Override public int getCount { return Integer MAX VALUE; } @Override public Object getItem int position { return null; } @Override public long getItemId int position { return 0; } @Override public View getView int position View convertView ViewGroup parent { LayoutInflater layoutInflater LayoutInflater from MainActivity this ; convertView layoutInflater inflate R layout picture null ; picture imageView ImageView convertView findViewById R id picture imageView ; picture imageView setImageResource ids1[position % 5] ; return convertView; } } @Override public void onItemSelected AdapterView< > parent View view int position long id { main imageView setImageResource ids1[position % 5] ; } @Override public void onNothingSelected AdapterView< > parent { } }">package e gallery; import android app Activity; import android os Bundle; import android view LayoutInflater; import android view View; import android view ViewGroup; import android view ViewGroup MarginLayoutParams; import android widget AdapterView; import android widget AdapterView OnItemSelecte [更多]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值