效果圖:
我的小霸王太卡了湖笨。
最近工作比較忙黄刚,今天搞了一下午才搞出來這個效果黔漂,這種效果有很多種實現方式诫尽,最常見的應該是用貝塞爾曲線實現的。今天我們來看另一種不同的實現方式炬守,只需要用到 canvas.scale()箱锐,有沒有很好奇是怎么實現的呢。
首先來說一下思路劳较,只要有了思路剩下的就是往里面套代碼了。
通過觀察上面的效果圖我們發(fā)現可以把右邊的字母分為三種類型
1浩聋、 手指沒觸摸的地方顯示正常的樣式
2观蜗、手指觸摸的位置 顯示最大且完全不透明
3、手指觸摸位置的上下附近位置 有放大且有透明度變化
對這個效果有了直觀的認識后衣洁,我們就可以在ondraw里面根據不同的條件來分別畫出這三種狀態(tài)墓捻,這里主要難理解的就是這些條件。這需要結合代碼看下。
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);
}
相信上面這些應該沒什么難度吧。 另外把一些需要的寬高屬性賦值一下梧兼,因為下面會用到它們
@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這個要注意一下它是索引列表的范圍放吩,但是我們并不需要畫出它。
2羽杰、在ontouch方法中對觸摸事件進行必要的處理
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;
}
這里面也很簡單渡紫,首先當手指按下時記錄下按下位置的Y坐標,然后判斷按下的位置是否在索引列表的區(qū)域范圍內(索引的區(qū)域在初始化賦值的時候已經確定過了)如果不在就沒必要執(zhí)行下去了 直接返回false即可考赛, 然后在手指移動的時候判斷下是否是在移動是的話就把mIsBeingDragger置為true惕澎,如果mIsBeingDragger為true說明正在移動 ,這時候就計算出當前手指所在的索引位置颜骤,并通過回調方式通知外面當前的位置唧喉,最后把索引位置賦給全局變量mChoose,并刷新UI忍抽。 手指抬起時進行一些復位操作八孝。 以上就是ontouch的全部方法。
3梯找、在ondraw方法里面畫出索引字母
這里要畫出那三種類型的字母索引唆阿,我們先從簡單的來
float lettersPos= mLetterHeight*(i+1)+getPaddingTop(); //下一個字母的Y值坐標
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中我們記錄的索引位置,當上面條件成立時說明當前就是選中的字母锈锤,這時候讓它縮放比例最大驯鳖,偏移量我們會在下面統(tǒng)一處理。 接下來處理不是選中的情況
float distanseDiff = Math.abs((mY - lettersPos)/mHeight);//計算手指觸摸位置的上下附近位置
float maxPos = distanseDiff * 7;//乘7是因為這個系數太小了需要給他一個放大
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;
}
這里主要就是那個縮放系數比較難算 需要多試下久免。 X Y方向的偏移量如下圖 這些都計算好后就可以畫了
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ā)現canvas.scale(diff,diff,mWidth*1.2f+diffX,lettersPos+diffY); 這一句才是整個自定義view的關鍵 它前兩個參數是x軸和y軸的縮放系數浅辙,后兩個參數是x軸和y軸的錨點,我主要是試出來的,這兩個參數比較難理解,還需要多家學習阎姥。到這里就已經實現了我們最上面的效果了记舆。
家里沒有github的環(huán)境所以只傳CSDN了。