Android自定义View实现角度选择器

 更新时间:2016年11月06日 11:26:26   投稿:daisy  
前几天在Google Photos查看照片,用了一下它的图片剪裁功能,于是我马上就被其界面和操作吸引。后来想模仿做一个和Google Photos裁图页面几乎一模一样的角度选择器,本文比较基础,在阅读本文前只需要掌握最基础的自定义View知识和Android事件知识。下面来一起学习下吧。

首先来看一下Google Photos的效果

实现最终的效果:

实现思路

仔细观察这个效果,先分析构成结构,我把它分成三部分:

    1、表示刻度的点

    2、相应点上方的数字

    3、控件中央的当前刻度与三角

可以看出,构成元素十分简单,不涉及图片,Drawable,那么只需要用Canvas画出来就好了。

接下来观察手势的操作,查看随着手指滑动,控件做出的变化,这里的变化有:

     1、手指按上去时,部分区域变亮(部分区域即为可见区域)

     2、随着手指滑动,相应的数字发生移动,当前角度值也发生改变

     3、离中心越近,透明度越低,且0°的下方的点要大一些

好了,我们对这个控件已经分析的很透彻了,根据分析,首先我们要在View的onDraw()方法中画出构成元素,之后要让它动起来,在onTouchEvent()方法中监听手势,改变一些值并重绘我们的View,这里很明显,我们要改变的肯定是当前角度这个值。

动手

既然有了思路,那么就要马上动手,不然下次忘记掉了怎么办?

画点

首先,先把点给画出来,位置很简单,肯定是从视图中心开始画,往左往右分别画点。

for (int i = 0; i < mPointCount; i++) {
 canvas.getClipBounds(mCanvasClipBounds);
 canvas.drawPoint(mCanvasClipBounds.centerX() + (i - mPointCount / 2) * mPointMargin,
     mCanvasClipBounds.centerY(), mPointPaint);
}

其中mPointCount为所要画的点个数,这里默认为51个,mPointMargin为两点间的边距,长度为View的宽度除以点的个数。

看看效果

嗯,成功的把点给画出来了。

画刻度上方的数字

既然把点给画出来了,根据我们的思路,我们要把数字给画到正确的位置上,画数字当然很简单,这里难的是找到正确的位置。

首先,我们的数字是会移动的,随着当前角度的不同,所展示的数字大小位置都不同。但毫无疑问的是,这个位置(x坐标)肯定是关于当前角度的一个函数。至于具体是一个什么样的函数,相信聪明的你很快就可以分析出来,毕竟只是一个线性关系,这里就直接贴代码了。

private void drawDegreeText(int degrees, Canvas canvas, boolean canReach) {
 canvas.drawText(degrees + "°",
    getWidth() / 2 + mPointMargin * degrees / 2 - mTextWidths[0] / 2 * 3 - mCurrentDegrees / 2 * mPointMargin,
     getHeight() / 2 - 10,
     mTextPaint);
 }
}

按照我们的效果,我们需要画出-90°~90°的刻度,其中-45°~45°是可到达角度,另外的角度不可到达。

drawDegreeText(0, canvas, true);
drawDegreeText(15, canvas, true);
drawDegreeText(30, canvas, true);
drawDegreeText(45, canvas, true);
drawDegreeText(-15, canvas, true);
drawDegreeText(-30, canvas, true);
drawDegreeText(-45, canvas, true);

drawDegreeText(60, canvas, false);
drawDegreeText(75, canvas, false);
drawDegreeText(90, canvas, false);
drawDegreeText(-60, canvas, false);
drawDegreeText(-75, canvas, false);
drawDegreeText(-90, canvas, false);

好了,来看一下效果,可以看到,数字被画在了正确的位置。

画当前角度

这个超级简单呢,位置也特别好确定,上方三角形的Path也非常好知道,但是这里要注意的是,当当前角度为0°左右时,不应该把0°刻度显示出来。

//画当前角度
if (mCurrentDegrees >= 10) {
 canvas.drawText(mCurrentDegrees + "°", getWidth() / 2 - mTextWidths[0], mBaseLine, mTextPaint);
} else if (mCurrentDegrees <= -10) {
 canvas.drawText(mCurrentDegrees + "°", getWidth() / 2 - mTextWidths[0] / 2 * 3, mBaseLine, mTextPaint);
} else if (mCurrentDegrees < 0) {
 canvas.drawText(mCurrentDegrees + "°", getWidth() / 2 - mTextWidths[0], mBaseLine, mTextPaint);
} else {
 canvas.drawText(mCurrentDegrees + "°", getWidth() / 2 - mTextWidths[0] / 2, mBaseLine, mTextPaint);
}

//三角指示器的Path
mIndicatorPath.moveTo(w / 2, h / 2 + mFontMetrics.top / 2 - 18);
mIndicatorPath.rLineTo(-8, -8);
mIndicatorPath.rLineTo(16, 0);

还有一个小细节,就是离中心越近,其透明度越来越低,也就是说,我们要根据离中心的位置,来改变画笔Paint的alpha值,再将点画出。

 for (int i = 0; i < mPointCount; i++) {

  if (i > zeroIndex - 22 && i < zeroIndex + 22 && mScrollStarted) {
   mPointPaint.setAlpha(255);
  } else {
   mPointPaint.setAlpha(100);
  }

  if (i > mPointCount / 2 - 8 && i < mPointCount / 2 + 8
    && i > zeroIndex - 22 && i < zeroIndex + 22) {
   if (mScrollStarted)
    mPointPaint.setAlpha(Math.abs(mPointCount / 2 - i) * 255 / 8);
   else
    mPointPaint.setAlpha(Math.abs(mPointCount / 2 - i) * 100 / 8);
  }
  ……
 }

这时,已经很像了,只是还不能动。

动起来

该绘制的部分我们已经都绘制起来了,是时候让这个View动起来了,角度选择器的触摸事件不复杂,我们只需要在我们移动手指的时候根据滑动距离来改变当前角度值并重绘视图就可以看到移动效果了。为什么呢?因为我们之前在画数字的时候,位置都是由当前角度这个值决定的,所以它一发生变化,数字的位置也会发生改变。

@Override
public boolean onTouchEvent(MotionEvent event) {
 switch (event.getAction()) {
  ……
  case MotionEvent.ACTION_MOVE:
   float distance = event.getX() - mLastTouchedPosition;
   ……
   if (distance != 0) {
    onScrollEvent(event, distance);
   }
   break;
  }
  ……
  return true;
}

private void onScrollEvent(MotionEvent event, float distance) {
 mTotalScrollDistance -= distance;
 postInvalidate();
 mLastTouchedPosition = event.getX();
 mCurrentDegrees = (int) ((mTotalScrollDistance * mDragFactor) / mPointMargin);
 if (mScrollingListener != null) {
  mScrollingListener.onScroll(mCurrentDegrees);
 }
}

当然你还需要处理一些细节性的东西,比如在数字移动的时候,靠近中心一定范围就会消失(透明度变为0),这些都是很容易控制的,只要改变画笔的透明度就好了,但是正是对细节的把控,才能做出一个效果让用户满意的自定义View。最后,再来看一下效果。

扩展

到这里,我们做的角度选择器已经和Google Photos的几乎一模一样了,但是,仅仅这样就够了。不,不够,我们还要继续扩展,为什么只能达到正负45°,我们要所有的范围自由选择,也就是-180°~180°,然后数字颜色啊,点的颜色啊都要让人自由选择。所以我们要扩展我们的角度选择器,把一切可以变化的接口暴露出来。

最后看一下我们多种多样的角度选择器,还是挺好看呀~

总结

以上就是这篇文章的全部内容了,这次的自定义View相对于前两次的来说,无疑是简单了很多,只运用了最基础的绘图知识和事件机制,但是看起来效果也还不错哦,嘿嘿,反正我特别喜欢这个角度选择器,虽然我还不知道除了裁图页面可以用到还有哪里可以用到。所以说运用最简单的知识,也可以组合出比较复杂的效果,希望大家多发挥自己的想象力,一起自定义出更多好玩的,实用的,酷炫的控件吧。希望这篇文章对你有帮助,哪怕只是一些启发,也是值得的。如果有疑问大家也可以留言交流。

相关文章

  • Android实现拍照、录像、录音代码范例

    Android实现拍照、录像、录音代码范例

    这篇文章主要介绍了Android实现拍照、录像、录音代码的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-10-10
  • Android消息处理机制Looper和Handler详解

    Android消息处理机制Looper和Handler详解

    Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行,本文将详细分析Android应用程序的消息处理机制
    2014-09-09
  • Android实现微信的图片选择器

    Android实现微信的图片选择器

    这篇文章主要为大家详细介绍了Android实现微信的图片选择器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • RecyclerView通过GridLayoutManager实现多样式布局的示例

    RecyclerView通过GridLayoutManager实现多样式布局的示例

    本篇文章主要介绍了RecyclerView通过GridLayoutManager实现多样式布局的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • android调用C语言实现内存的读取与修改的方法示例

    android调用C语言实现内存的读取与修改的方法示例

    这篇文章主要介绍了android调用C语言实现内存的读取与修改的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Android给TextView添加点击事件的实现方法

    Android给TextView添加点击事件的实现方法

    下面小编就为大家带来一篇Android给TextView添加点击事件的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • Android handle-message的发送与处理案例详解

    Android handle-message的发送与处理案例详解

    这篇文章主要介绍了Android handle-message的发送与处理案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Android注解ButterKnife的基本使用

    Android注解ButterKnife的基本使用

    这篇文章主要介绍了Android注解ButterKnife的基本使用的相关资料,需要的朋友可以参考下
    2017-01-01
  • Android获取assets文件夹中的数据并写入SD卡示例

    Android获取assets文件夹中的数据并写入SD卡示例

    这篇文章主要介绍了Android获取assets文件夹中的数据并写入SD卡示例,对初学Android开发的朋友来说是一个很实用的功能,需要的朋友可以参考下
    2014-07-07
  • Android如何防止apk程序被反编译(尊重劳动成果)

    Android如何防止apk程序被反编译(尊重劳动成果)

    作为Android应用开发者,不得不面对一个尴尬的局面,就是自己辛辛苦苦开发的应用可以被别人很轻易的就反编译出来,天下痛苦之事莫过于此啊,本文会介绍一种防止apk程序被反编译的方法,感兴趣的朋友可以了解下哦
    2013-01-01

最新评论