自定义组件 滑动列表组件思路

自定义组件:实现adapter动态管理子项,view高度自适应子项。可左右滑动,view中心位置为选中位。滑动到中间区域的子项自动放大高亮,其他缩小半透明。滑动手势结束可以惯性滑动到最近的子项选中状态。左右边界允许拉伸最多一个子项的距离,松开自动回弹到选中位置。

实现思路:

 (思路来源参考goeasyway

1,onMeasure  (measureChild)循环测量子view (如需自适应大小可以在 测量完child后通过。setMeasuredDimension设置)

      onLayout 获取子view 宽高 通过自己的布局逻辑确定每个child的位置(childView.layout) 计算  viewgroup的总宽高

2, onInterceptTouchEvent 在合适的时机返回true 消费事件

3,onTouchEvent 中处理触摸滑动方向及距离,通过scrollTo scrollBy 配合

      在ACTION_UP 计算当前显示的item,并处理惯性

4,new Scroller(context) 使用scroller 实现惯性滑动过程

      调用startScroll设置开始惯性滑动 invalidate()刷新界面

       重写computeScroll()实现惯性并处理其他逻辑

5,removeAllViewsInLayout/addViewInLayout 使用等配合实现adapter动态添加更改child

实现代码: 

一:使用

<com.sinovatio.demo.CoverflowView
    android:id="@+id/c_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</com.sinovatio.demo.CoverflowView>
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    CoverflowView coverflowView = findViewById(R.id.coverflowview);
    coverflowView.setAdapter(new MyAdapter());
    coverflowView.setSelection(3);
}

 private class MyAdapter extends BaseAdapter {

     @Override
     public int getCount() {
         return 5;
     }

     @Override
     public Object getItem(int position) {
         return position;
     }

     @Override
     public long getItemId(int position) {
         return position;
     }

     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         View view = null;
         if (convertView == null) {
             view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, parent,false);
         } else {
             view = convertView;
         }
         view.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 Log.i("computeScroll",System.currentTimeMillis()+"");
             }
         });
         return view;
     }
 }

二:viewGroup

public class CoverflowView extends ViewGroup {

    private int touchSlop;
    private float xLastDown;
    private float xLastMove;

    private int leftBorder;
    private int rightBorder;
    private int childWidth;
    private int maxHeight=0;

    private Scroller scroller;

    private Adapter adapter;
    private int selection = 0;

    private int viewWidth=0;


    public CoverflowView(Context context) {
        this(context, null);
    }

    public CoverflowView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CoverflowView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        touchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
        scroller = new Scroller(context);
    }

    public void setAdapter(Adapter adapter) {
        this.adapter = adapter;
        reset();
    }

    public Adapter getAdapter() {
        return adapter;
    }

    public void setSelection(int selection) {
        if (adapter == null) {
            throw new IllegalStateException("Error: Adapter is null.");
        }
        if (selection < 0 || selection > (adapter.getCount() - 1)) {
            throw new IllegalArgumentException("Position index must be in range of adapter values (0 - getCount()-1)");
        }
        this.selection = selection;
        reset();
    }

    public int getSelection() {
        return selection;
    }

    private void reset() {
        if (adapter == null || adapter.getCount() == 0) {
            return;
        }

        removeAllViewsInLayout();
//        removeAllViews();

        int count = adapter.getCount();
        for (int i = 0; i < count; i++) {
            View view = adapter.getView(i, null, this);
            /**
             *  view.getLayoutParams()   new LayoutParams(320, 320)
             *  自适应高度需要获取子项的LayoutParams,就需要子项添加是使用当前为parent。否则或报错
             */
            addViewInLayout(view, 0, view.getLayoutParams(), true);
        }

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewWidth = w;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View v = getChildAt(i);
            //测量子view 使其知道自己的大小
            measureChild(v, widthMeasureSpec, heightMeasureSpec);
            //此时才可以得到其大小
            int height = v.getMeasuredHeight();
            if(maxHeight<=height){
                maxHeight=height;
            }
//            viewWidth+=v.getMeasuredWidth();
        }
        //自适应高度
        setMeasuredDimension(widthMeasureSpec,maxHeight+14);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int painterPosX = l;
        int painterPosY = t;

        int childCount = getChildCount();
        // 使用了比较简单直接的做法(当然不是最优化的做法),直接水平线性布局所有的子View
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            int width = childView.getMeasuredWidth();
            int height = childView.getMeasuredHeight();
            //为子view布局
            childView.layout(painterPosX, painterPosY, painterPosX + width, painterPosY + height);
            painterPosX += width;
            childWidth = width;
        }

        leftBorder = l - viewWidth / 2;
        rightBorder = painterPosX ;

        scrollToSelectionCenter();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
//                Log.i("computeScroll4",System.currentTimeMillis()+"");
                xLastDown = ev.getRawX();
                break;
            case MotionEvent.ACTION_MOVE:
//                Log.i("computeScroll3",System.currentTimeMillis()+"");
                xLastMove = ev.getRawX();
                float deltaX = xLastMove - xLastDown;
                //在发生滑动是拦截事件交给自己onTouchEvent消费
                if (Math.abs(deltaX) > touchSlop) {
                    return true;
                }
                break;
                case MotionEvent.ACTION_UP:
//                    Log.i("computeScroll0",System.currentTimeMillis()+"");
                    break;
        }
//        Log.i("computeScroll2",System.currentTimeMillis()+"");
        //其他时间不对事件处理交给child
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                float xMove = event.getRawX();
                int deltaX = (int) (xLastMove - xMove);
                if ((getScrollX() + deltaX) < leftBorder) {
                    scrollTo(leftBorder, 0);
                } else if ((getScrollX() + childWidth + deltaX) > rightBorder) {
                    scrollTo(rightBorder - childWidth, 0);
                }
                scrollBy(deltaX, 0); // 你不得不自己试一下scrollBy与scrollTo的区别。
                xLastMove = xMove;
                updateSelection();
                break;
            case MotionEvent.ACTION_UP:
//                Log.i("computeScroll",scroller.computeScrollOffset()+"");
                scrollToSelectionCenter();
                break;
        }

        return super.onTouchEvent(event);
    }

    /**
     * 根据当间移动的位置确定被先中的Items
     */
    private void updateSelection() {
        selection = (getScrollX() + viewWidth / 2) / childWidth;
        if (selection < 0) {
            selection = 0;
        } else if (selection > adapter.getCount() - 1) {
            selection = adapter.getCount() - 1;
        }
    }

    /**
     * 将选中的Item在ViewGroup的中央显示
     */
    private void scrollToSelectionCenter() {
        if (adapter == null || adapter.getCount() == 0) {
            return;
        }
        if (selection < 0 || selection > adapter.getCount() - 1) {
            return;
        }
        View viewChild = getChildAt(selection);
        int dx = (selection * childWidth + childWidth / 2) - getScrollX() - viewWidth / 2;
        //开始惯性滑动
        scroller.startScroll(getScrollX(), 0, dx, 0);
        invalidate();

    }

    @Override
    public void computeScroll() {
        //检查滑动是否结束
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
//            Log.i("computeScroll",scroller.getCurrX()+"  "+scroller.getCurrY());
            invalidate();
        }
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {

            View view = getChildAt(i);
            if (i == selection) {
                view.setScaleX(1f);
                view.setScaleY(1f);
                view.setAlpha(1f);
            } else { // 没有选中的Item缩小并变半透明
                view.setScaleX(0.75f);
                view.setScaleY(0.75f);
                view.setAlpha(0.35f);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值