Android 弧形轉盤的實現,弧形列表;

各位看官,下面是效果圖(自己手殘打了一下碼),請先過目:

簡單的說一下效果圖,橫向來區分的話可以把UI圖分爲三部分【左邊是輪盤 , 中間是弧形列表 也是一級菜單 , 右邊是列表是二級菜單(不重要沒有圖)】:

要求:

  1. 實現一個圍繞輪盤的弧形列表;
  2. 弧形列表滑動後自動選中居中的條目,然後更新右邊的二級菜單;
  3. 弧形列表點擊後自動滑動到居中位置並選中,然後更新右邊的二級菜單;
  4. 弧形列表所有條目都可以選擇;
  5. 左邊的輪盤跟隨弧形列表旋轉;

好的、需求很明確,一級菜單弧形列表可滑可點自動選中,輪盤跟隨旋轉;

第一步:先實現弧形列表;

然後開始找輪子,各大網站找個一圈都沒有發現類似的開源框架;發現這個'https://github.com/kHRYSTAL/CircleRecyclerView' 可以實現弧形列表,下載下來試了一下有些差別,不能所有的條目都選中(第一個條目拉不下來),未選中的沒有點擊事件;後續又找了幾個類似的文章,跟效果都有點差別,無奈只能自己寫了;

實現弧形列表的第一想法時自定義RecyclerView的LayoutManager,後來參考了一下這篇文章(https://github.com/dkmeteor/CircleList)用設置偏移量實現RecyclerView弧形列表;

實現方式一 ,矩陣偏移:

1、先自定義Layout實現偏移 , RecyclerView的Item佈局的根佈局使用這個Layout;

public class MatrixTranslateLayout extends LinearLayout {
    private int parentHeight = 0;
    private int topOffset = 0;
    public MatrixTranslateLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setParentHeight(int height) {
        parentHeight = height;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.save();
        if (topOffset == 0) {
            topOffset = getHeight() / 2;
        }
        int top = getTop()+topOffset;

       float tran = calculateTranslate(top , parentHeight);

        Matrix m = canvas.getMatrix();
        m.setTranslate(tran,0);
        canvas.concat(m);
        super.dispatchDraw(canvas);
        canvas.restore();
    }

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


    private float calculateTranslate(int top, int h) {
        float result = 0f;
        int hh = h/2;
        result = Math.abs(top - hh)*-1;
        return (float) (result/2);
    }
}

2、RecyclerView的Adapter填充佈局時設置矩陣偏移量;

class MAdapter extends RecyclerView.Adapter {
        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            return new MAdapter.VH(LayoutInflater.from(ArcActivity.this).inflate(R.layout.item_arc, parent, false));
        }

        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            MAdapter.VH vh = (MAdapter.VH) holder;
            ((MatrixTranslateLayout) vh.itemView).setParentHeight(recyclerView.getHeight());
            vh.tv.setText(mDatas.get(position));
            final int fp = position;
            vh.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    TUtils.show(ArcActivity.this, "點擊" +mDatas.get(fp));
                }
            });
        }

        @Override
        public int getItemCount() {
            return mDatas.size();
        }

        class VH extends RecyclerView.ViewHolder {

            public TextView tv;

            public VH(@NonNull View itemView) {
                super(itemView);
                tv = itemView.findViewById(R.id.tv);
            }
        }
    }

切記:要等佈局完全加載後再設置Adapter , 不然會拿不到RecyclerView的高度;

recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                findView();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            }
        });

3、當RecyclerView滑動時重新調整矩陣偏移量;

 private void findView() {
        mAdapter = new MAdapter();
        recyclerView.setAdapter(mAdapter);

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                for (int i = 0; i < recyclerView.getChildCount(); i++) {
                    recyclerView.getChildAt(i).invalidate();
                }
            }
        });

    }

這樣一個弧形RecyclerView就實現了;

實現方式二 ,內邊距偏移:

不需要自定義Layout , 只需要改變Item根佈局的PaddingLift即可;

根據方式一的思路換了一個實現方式,具體看代碼:

recyclerView.setLayoutManager(new LinearLayoutManager(this));

        recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                recyclerViewHeight = recyclerView.getHeight();
                setData();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            }
        });
    private void setData() {
        recyclerView.setAdapter(new RecyclerView.Adapter() {
            @NonNull
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                return new VH(LayoutInflater.from(PadingArcActivity.this).inflate( R.layout.item_pading,parent,false));
            }

            @Override
            public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
                VH vh = (VH) holder;
                RelativeLayout mv = (RelativeLayout) vh.itemView;
                mv.setPadding(calculateTranslate(mv.getTop() , recyclerViewHeight) , 0 ,0 ,0 );
                vh.tv.setText("你好"+position+"索引");
                final int fp = position;
                vh.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        TUtils.show(PadingArcActivity.this , "你好"+fp+"索引");
                    }
                });
            }

            @Override
            public int getItemCount() {
                return 100;
            }

            class VH extends RecyclerView.ViewHolder{

                public TextView tv;
                public VH(@NonNull View itemView) {
                    super(itemView);
                    tv = itemView.findViewById(R.id.tv);
                }
            }
        });

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                for (int i = 0; i < recyclerView.getChildCount(); i++) {
                    int pad =  calculateTranslate(recyclerView.getChildAt(i).getTop() , recyclerViewHeight);
                    recyclerView.getChildAt(i).setPadding( pad, 0 ,0 ,0 );
                }
            }
        });
    }

    private int calculateTranslate(int top, int h) {
        int result = 0;
        h = h - UiUtils.dip2px(this , 60); //減去當前控件的高度,(60是已知當前Item的高度)
        int hh = h/2;
        result = Math.abs(hh - top);
        result = hh - result;
        return result/2;
    }

這樣通過改變內邊距的方式也可以實現弧形列表;

佈局這種小兒科的內容就不貼出來了,下面是弧形列表實現後的效果;

 

 

Item的View就只寫了一個TextView , 看着比較粗糙;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章