效果图:

我的小霸王太卡了。

最近工作比较忙,今天搞了一下午才搞出来这个效果,这种效果有很多种实现方式,最常见的应该是用贝塞尔曲线实现的。今天我们来看另一种不同的实现方式,只需要用到 canvas.scale(),有没有很好奇是怎么实现的呢。

首先来说一下思路,只要有了思路剩下的就是往里面套代码了。

通过观察上面的效果图我们发现可以把右边的字母分为三种类型

1、 手指没触摸的地方显示正常的样式

2、手指触摸的位置 显示最大且完全不透明

3、手指触摸位置的上下附近位置 有放大且有透明度变化

对这个效果有了直观的认识后,我们就可以在ondraw里面根据不同的条件来分别画出这三种状态,这里主要难理解的就是这些条件。这需要结合代码看下。

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坐标,然后判断按下的位置是否在索引列表的区域范围内(索引的区域在初始化赋值的时候已经确定过了)如果不在就没必要执行下去了 直接返回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中我们记录的索引位置,当上面条件成立时说明当前就是选中的字母,这时候让它缩放比例最大,偏移量我们会在下面统一处理。

接下来处理不是选中的情况

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);
            }
            Log.i("lly","mLetters["+i+"] = " +mLetters[i] );
            canvas.drawText(mLetters[i],mWidth,lettersPos,mPaint);
            canvas.restore();

可以发现canvas.scale(diff,diff,mWidth*1.2f+diffX,lettersPos+diffY); 这一句才是整个自定义view的关键 它前两个参数是x轴和y轴的缩放系数,后两个参数是x轴和y轴的锚点,我主要是试出来的,这两个参数比较难理解,还需要多家学习。到这里就已经实现了我们最上面的效果了。

源码

android自定义View之3D索引效果的更多相关文章

  1. Android 自定义view实现水波纹效果

    http://blog.csdn.net/tianjian4592/article/details/44222565 在实际的开发中,很多时候还会遇到相对比较复杂的需求,比如产品妹纸或UI妹纸在哪看了 ...

  2. android自定义view实现progressbar的效果

    一键清理是很多Launcher都会带有的功能,其效果也比较美观.实现方式也许有很多中,其中常见的是使用图片drawable来完成的,具体可以参考这篇文章:模仿实现360桌面水晶球式的一键清理特效.本文 ...

  3. Android自定义View——刮刮卡效果

    想要红包的实现效果的可以关注我的博客,仿饿了么红包 下层图片:我们的红包的图片 上层图片:有两部分 一部分是灰色背景 一部分是拥有透明度为0,并且模式为交集的画笔 使用滑动监听,滑动时,用透明度为0的 ...

  4. Android自定义View 画弧形,文字,并增加动画效果

    一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类   B ...

  5. Android自定义View(LimitScrollerView-仿天猫广告栏上下滚动效果)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53303872 本文出自:[openXu的博客] 1分析 2定义组合控件布局 3继承最外层控件 ...

  6. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  7. Android自定义View和控件之一-定制属于自己的UI

    照例,拿来主义.我的学习是基于下面的三篇blog.前两是基本的流程,第三篇里有比较细致的绘制相关的属性.第4篇介绍了如何减少布局层次来提高效率. 1. 教你搞定Android自定义View 2. 教你 ...

  8. Android自定义View(CustomCalendar-定制日历控件)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/54020386 本文出自:[openXu的博客] 目录: 1分析 2自定义属性 3onMeas ...

  9. 简单说说Android自定义view学习推荐的方式

    这几天比较受关注,挺开心的,嘿嘿. 这里给大家总结一下学习自定义view的一些技巧.  以后写自定义view可能不会写博客了,但是可以开源的我会把源码丢到github上我的地址:https://git ...

随机推荐

  1. Java面试题—初级(4)

    31.String s = new String("xyz");创建了几个StringObject?是否可以继承String类? 两个或一个都有可能,"xyz" ...

  2. angularjs中的run()方法使用

    run方法用于初始化全局的数据,仅对全局作用域起作用. 举个栗子吧:<script type="text/javascript"> var m1 = angular.m ...

  3. 使用 C# (.NET Core) 实现命令设计模式 (Command Pattern)

    本文的概念内容来自深入浅出设计模式一书. 项目需求 有这样一个可编程的新型遥控器, 它有7个可编程插槽, 每个插槽可连接不同的家用电器设备. 每个插槽对应两个按钮: 开, 关(ON, OFF). 此外 ...

  4. win10安装Ubuntu14.04双系统

    1 制作镜像 UltralISO刻录镜像到U盘,下载地址:http://pan.baidu.com/s/1o7JpthS 2压缩空间给Ubuntu安装 使用windows自带的压缩(磁盘管理) 3安装 ...

  5. Python中if __name__ == "__main__": 的理解

    1.在很多python脚本中在最后的部分会执行一个判断语句if __name__ == "__main__:",之后还可能会有一些执行语句.那添加这个判断的目的何在? 在pytho ...

  6. java代码优化细节

    在代码线上运行的过程中,往往会出现很多我们意想不到的错误,不少错误定位到最后往往是一个非常小的原因导致的.然而因为线上环境和开发环境是非常不同的,为了解决一个错误,我们需要先查找错误原因.修改验证.打 ...

  7. [C#]设计模式-单例模式-创建型模式

    单例模式用于在整个软件系统当中保持唯一实例,在 C# 当中最能够体现此概念的就是静态类,静态类的生命周期是跟随整个程序,并且在整个程序中仅保有一个实例. 不过在这里我们不再详细阐述单例模式与静态类有什 ...

  8. 2018.4.16Spring.Net入门学习内容

    三大方面: IoC:Inversion of Control 控制翻转:就是创建对象的权利由开发人员自己控制New,转到了由容器来控制. DI:Dependency InjectionIt is a ...

  9. codefroces 297E Mystic Carvings

    problem:一个圆上依次有1~2*n的数字.每个数字都有且只有另一个数字与他相连.选出三条线,使得每条线的两端之间隔的最少点(只包括被选择的6个点)的个数相等.输入输出格式输入格式: The fi ...

  10. 【BZOJ2186】【SDOI2008】沙拉公主的困惑

    Description ​ 大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票.房地产第一大户沙拉公主决定预测一下大富翁 ...