android自定義View之3D索引效果

QQ圖片20161211090532.png

效果圖:
這里寫圖片描述

我的小霸王太卡了。

最近工作比較忙,今天搞了一下午才搞出來這個(gè)效果,這種效果有很多種實(shí)現(xiàn)方式,最常見的應(yīng)該是用貝塞爾曲線實(shí)現(xiàn)的。今天我們來看另一種不同的實(shí)現(xiàn)方式,只需要用到 canvas.scale(),有沒有很好奇是怎么實(shí)現(xiàn)的呢。

首先來說一下思路,只要有了思路剩下的就是往里面套代碼了。
通過觀察上面的效果圖我們發(fā)現(xiàn)可以把右邊的字母分為三種類型
1、 手指沒觸摸的地方顯示正常的樣式
2、手指觸摸的位置 顯示最大且完全不透明
3、手指觸摸位置的上下附近位置 有放大且有透明度變化

對這個(gè)效果有了直觀的認(rèn)識后,我們就可以在ondraw里面根據(jù)不同的條件來分別畫出這三種狀態(tài),這里主要難理解的就是這些條件。這需要結(jié)合代碼看下。

so 我們開始擼碼吧,

1、先初始化一些需要的變量


private void init(Context context) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.GRAY);
        mLetters = context.getResources().getStringArray(R.array.letter_list);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mDensity  = getContext().getResources().getDisplayMetrics().density;
        setPadding(0,dip2px(20),0,dip2px(20));
    }

 private int dip2px(int dipPx){
        return (int)(dipPx*mDensity+0.5);
    }

相信上面這些應(yīng)該沒什么難度吧。 另外把一些需要的寬高屬性賦值一下,因?yàn)橄旅鏁玫剿鼈?/p>

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

        mHeight = h - getPaddingTop() - getPaddingBottom();
        mWidth = w - dip2px(16);
        mLetterHeight = mHeight / mLetters.length;
        int textSize = (int)(mLetterHeight*0.7);
        mPaint.setTextSize(textSize);
        mIsDownRect.set(w-dip2px(32),0,w,h);
    }

這里主要就是mIsDownRect這個(gè)要注意一下它是索引列表的范圍,但是我們并不需要畫出它。
2、在ontouch方法中對觸摸事件進(jìn)行必要的處理

public boolean onTouchEvent(MotionEvent event) {
       int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                mIsBeingDragger = false;
                float initDownY = event.getY();
                if(!mIsDownRect.contains(event.getX(),event.getY())){
                    return false;
                }
                mInitDownY = initDownY;
                break;
            case MotionEvent.ACTION_MOVE:
                float y = event.getY();
                float diff = Math.abs(y - mInitDownY);
                if(diff>mTouchSlop&&!mIsBeingDragger){
                    mIsBeingDragger = true;
                }
                if(mIsBeingDragger){
                    mY = y;
                    float moveY = y - getPaddingTop();
                    int chartIndex = (int) (moveY / mHeight * mLetters.length);//獲取索引位置的index
                    if(mChoose!=chartIndex){
                        if(chartIndex>=0&&chartIndex<mLetters.length) {
                            if (slidViewListener != null) {
                                Log.i("lly","chartIndex = "+chartIndex);
                                slidViewListener.onChange(mLetters[chartIndex]);
                            }
                            mChoose = chartIndex;
                        }
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsBeingDragger = false;
                mChoose = -1;
                invalidate();
                break;
        }

        return true;
    }

這里面也很簡單,首先當(dāng)手指按下時(shí)記錄下按下位置的Y坐標(biāo),然后判斷按下的位置是否在索引列表的區(qū)域范圍內(nèi)(索引的區(qū)域在初始化賦值的時(shí)候已經(jīng)確定過了)如果不在就沒必要執(zhí)行下去了 直接返回false即可, 然后在手指移動的時(shí)候判斷下是否是在移動是的話就把mIsBeingDragger置為true,如果mIsBeingDragger為true說明正在移動 ,這時(shí)候就計(jì)算出當(dāng)前手指所在的索引位置,并通過回調(diào)方式通知外面當(dāng)前的位置,最后把索引位置賦給全局變量mChoose,并刷新UI。 手指抬起時(shí)進(jìn)行一些復(fù)位操作。 以上就是ontouch的全部方法。
3、在ondraw方法里面畫出索引字母

這里要畫出那三種類型的字母索引,我們先從簡單的來

  float lettersPos= mLetterHeight*(i+1)+getPaddingTop(); //下一個(gè)字母的Y值坐標(biāo)
            float diffY; // Y 方向的偏移量
            float diffX;//X 方向的偏移量
            float diff;//縮放比例
  if (mChoose == i&&i!=0&&i!=mLetters.length-1) {
      diff = 2.2f;
      diffX=0f;
      diffY=0f;
  }

mChoose 是在ontouch中我們記錄的索引位置,當(dāng)上面條件成立時(shí)說明當(dāng)前就是選中的字母,這時(shí)候讓它縮放比例最大,偏移量我們會在下面統(tǒng)一處理。 接下來處理不是選中的情況

float distanseDiff = Math.abs((mY - lettersPos)/mHeight);//計(jì)算手指觸摸位置的上下附近位置
                float maxPos = distanseDiff * 7;//乘7是因?yàn)檫@個(gè)系數(shù)太小了需要給他一個(gè)放大
                if(distanseDiff<0.174){
                    diff = 2.2f - maxPos;
                }else {
                    diff = 1f;
                }

                if(!mIsBeingDragger){
                    diff =1;
                }
                diffX  = maxPos *  50;
                if(mY>lettersPos){
                    diffY = maxPos*50;
                }else {
                    diffY = - maxPos*50;
                }

這里主要就是那個(gè)縮放系數(shù)比較難算 需要多試下。 X Y方向的偏移量如下圖
這里寫圖片描述

這些都計(jì)算好后就可以畫了

 canvas.save();
            canvas.scale(diff,diff,mWidth*1.2f+diffX,lettersPos+diffY);
            if(diff ==1){
                mPaint.setAlpha(255);
                mPaint.setTypeface(Typeface.DEFAULT);

            }else {
                int alpha = (int) (255*(1-Math.min(0.9,diff -1)));
                if(mChoose == i){
                    alpha = 255;

                }
                mPaint.setAlpha(alpha);
                mPaint.setTypeface(Typeface.DEFAULT_BOLD);
            }

可以發(fā)現(xiàn)canvas.scale(diff,diff,mWidth*1.2f+diffX,lettersPos+diffY); 這一句才是整個(gè)自定義view的關(guān)鍵 它前兩個(gè)參數(shù)是x軸和y軸的縮放系數(shù),后兩個(gè)參數(shù)是x軸和y軸的錨點(diǎn),我主要是試出來的,這兩個(gè)參數(shù)比較難理解,還需要多家學(xué)習(xí)。到這里就已經(jīng)實(shí)現(xiàn)了我們最上面的效果了。

家里沒有g(shù)ithub的環(huán)境所以只傳CSDN了。

源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容