android 自定义searchview,android自定义view--SearchView
前言
上一篇Path特效功臣----PathMesure我们讲了PathMesure中api的详细方法和测试。本文就用我们学到的PathMeasure实现一个动态效果的SearchView,先瞄一下好不好看
searchView图片来源于网上
思路(由简到繁)
绘制静态的路径(放大镜和外圆)
1、onSizeChang()中得到组件大小
2、填充Path的放大镜和外圆
为静态图添加动态效果
1、采用ValueAnimtor提供实时变量
2、采用PathMeasure根据实时变量去绘制
3、动态效果分为四种状态(初始化,放大镜动画,外圆动画,结束动画)
绘制静态的路径
绘制的路径全部由Path去填充,放大镜可以拆分为一个圆和一条斜线,需要注意的是这里addArc的起始角度和终点角度
红色为Path的起点45度
class MySearchView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
constructor(context: Context?) : this(context, null)
private val mPaint = Paint()//画笔
private var offestFactoty = 0.9f //偏移因子画布是组件的0.9
private var mWidth: Float = 0.0f //组件宽度
private var mHeight: Float = 0.0f //组件高度
private lateinit var searchRecf: RectF
private lateinit var cicleRecf: RectF
private val searchPath: Path = Path() //放大镜的path
private val ciclePath: Path = Path() //外圆的path
init {
initPaint()
}
private fun initPaint() {
mPaint.color = Color.BLUE
mPaint.isAntiAlias = true
mPaint.style = Paint.Style.STROKE
mPaint.strokeCap = Paint.Cap.ROUND
mPaint.strokeWidth = 8f
}
//1、onSizeChang()中得到组件大小
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mWidth = w.toFloat()
mHeight = h.toFloat()
initPath()
}
//2、填充Path的放大镜和外圆
private fun initPath() {
cicleRecf = RectF(-mWidth / 2 * offestFactoty, -mHeight / 2 * offestFactoty, mWidth / 2 * offestFactoty, mHeight / 2 * offestFactoty)
searchRecf = RectF(-mWidth / 5, -mHeight / 5, mWidth / 5, mHeight / 5)
searchPath.addArc(searchRecf, 45f, 359.9f) //填充内圆
ciclePath.addArc(cicleRecf, 45f, 359.9f) //填充外圆
val pathMeasure = PathMeasure(ciclePath, false)
val floatArray = FloatArray(2)
val posTan = pathMeasure.getPosTan(0f, floatArray, null)//拿到手柄的终点
searchPath.lineTo(floatArray[0], floatArray[1])//为内圆添加手柄路径形成放大镜
}
override fun onDraw(canvas: Canvas?) {
canvas?.translate(mWidth / 2, mHeight / 2)//移动坐标到组件中心
canvas?.drawPath(searchPath, mPaint)
canvas?.drawPath(ciclePath,mPaint)
}
}
静态图
为静态图添加动态效果
package com.hzb.myutils.view
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import com.hzb.utilsbox.utils.LogUtil
class MySearchView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
constructor(context: Context?) : this(context, null)
private val mPaint = Paint()//画笔
private var offestFactoty = 0.9f //偏移因子画布是组件的0.9
private var mWidth: Float = 0.0f //组件宽度
private var mHeight: Float = 0.0f //组件高度
private lateinit var searchRecf: RectF
private lateinit var cicleRecf: RectF
private val searchPath: Path = Path() //放大镜的path
private val ciclePath: Path = Path() //外圆的path
private var viewStatus = AnimStatus.NONE
private var isTopAnim:Boolean=false
private var animatorValue: Float = 0.0f //动画变量,0-1
//属性动画
private lateinit var startAnim: ValueAnimator
private lateinit var searingAnim: ValueAnimator
private lateinit var endAnim: ValueAnimator
private enum class AnimStatus { //标志动画状态
NONE, START, SEARING, END//初始状态,开始搜索,搜索中,结束搜索
}
init {
initPaint()
initAnimator()
initEvent()
}
private fun initAnimator() {
//AnimStatus.START状态的动画
startAnim = ValueAnimator.ofFloat(0f, 1f)
startAnim.duration = 1000
startAnim.addUpdateListener { animation ->
animatorValue = animation.animatedValue as Float
invalidate()
}
//AniStatus.SEARING状态动画
searingAnim = ValueAnimator.ofFloat(0f, 1f)
searingAnim.interpolator = LinearInterpolator()
searingAnim.duration=1500
searingAnim.repeatCount=ValueAnimator.INFINITE
searingAnim.repeatMode=ValueAnimator.RESTART
searingAnim.addUpdateListener { animation ->
if (viewStatus==AnimStatus.SEARING) { //这里必须添加,不然放大镜会有一刹那全部显示
animatorValue = animation.animatedValue as Float
invalidate()
}
}
//AniStatus.END状态动画
endAnim = ValueAnimator.ofFloat(1f, 0f)
endAnim.duration=1000
endAnim.addUpdateListener { animation ->
animatorValue = animation.animatedValue as Float
invalidate()
}
}
private fun initPaint() {
mPaint.color = Color.BLUE
mPaint.isAntiAlias = true
mPaint.style = Paint.Style.STROKE
mPaint.strokeCap = Paint.Cap.ROUND
mPaint.strokeWidth = 8f
}
1、onSizeChang()中得到组件大小
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mWidth = w.toFloat()
mHeight = h.toFloat()
initPath()
}
//2、填充Path的放大镜和外圆
private fun initPath() {
cicleRecf = RectF(-mWidth / 2 * offestFactoty, -mHeight / 2 * offestFactoty, mWidth / 2 * offestFactoty, mHeight / 2 * offestFactoty)
searchRecf = RectF(-mWidth / 5, -mHeight / 5, mWidth / 5, mHeight / 5)
searchPath.addArc(searchRecf, 45f, 359.9f)
ciclePath.addArc(cicleRecf, 45f, 359.9f)
val pathMeasure = PathMeasure(ciclePath, false)
val floatArray = FloatArray(2)
val posTan = pathMeasure.getPosTan(0f, floatArray, null)
searchPath.lineTo(floatArray[0], floatArray[1])//放大镜的手柄
}
override fun onDraw(canvas: Canvas?) {
canvas?.translate(mWidth / 2, mHeight / 2)//移动坐标到组件中心
when (viewStatus) {
AnimStatus.NONE -> { //初识转态
canvas?.drawPath(searchPath, mPaint)
}
AnimStatus.START -> { //开始搜索
val pathMeasure = PathMeasure(searchPath, false)
val dst = Path()
pathMeasure.getSegment(pathMeasure.length * animatorValue, pathMeasure.length, dst, true)
canvas?.drawPath(dst, mPaint)
}
AnimStatus.SEARING -> { //搜索中
val pathMeasure = PathMeasure(ciclePath, false)
val dst = Path()
val stop = pathMeasure.length * animatorValue
val start = (stop - (0.5 - Math.abs(animatorValue - 0.5)) * pathMeasure.length/2).toFloat()
pathMeasure.getSegment(start,stop, dst, true)
canvas?.drawPath(dst, mPaint)
}
AnimStatus.END -> { //搜索结束
val pathMeasure = PathMeasure(searchPath, false)
val dst = Path()
pathMeasure.getSegment(pathMeasure.length*animatorValue , pathMeasure.length, dst, true)
canvas?.drawPath(dst, mPaint)
}
}
}
/**
* 开始动画
*/
fun startAnim() {
viewStatus=AnimStatus.START
startAnim.start()
this.isClickable = false
}
/**
* 结束动画
*/
fun stopAnim(){
isTopAnim=true
}
/**
* 监听动画结束
*/
private fun initEvent() {
val listener: Animator.AnimatorListener = object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
if (isTopAnim) {
animation?.cancel()
viewStatus=AnimStatus.END
endAnim.start()
}
}
override fun onAnimationEnd(animation: Animator?) {
val name = Thread.currentThread().name
LogUtil.i(name)
when (viewStatus) {
AnimStatus.START -> {
searingAnim.start()
viewStatus=AnimStatus.SEARING
}
AnimStatus.END -> {
viewStatus = AnimStatus.NONE
this@MySearchView.isClickable = true
isTopAnim=false
}
}
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
}
startAnim.addListener(listener)
searingAnim.addListener(listener)
endAnim.addListener(listener)
}
}
运行效果
效果gif
android 自定义searchview,android自定义view--SearchView相关推荐
- android炫酷的自定义view,Android自定义View实现炫酷进度条
本文实例为大家分享了Android实现炫酷进度条的具体代码,供大家参考,具体内容如下 下面我们来实现如下效果: 第一步:创建attrs文件夹,自定义属性: 第二步:自定义View: /** * Cre ...
- android组件什么时候加载到r文件,Android自定义加载loading view动画组件
我写写使用步骤 自定义view(CircleProgress )的代码 package com.hysmarthotel.view; import com.hysmarthotel.roomcontr ...
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]
http://blog.csdn.net/jj120522/article/details/8095852 示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这 ...
- android sqlite自定义函数,Android中自定义一个View的方法详解
本文实例讲述了Android中自定义一个View的方法.分享给大家供大家参考,具体如下: Android中自定义View的实现比较简单,无非就是继承父类,然后重载方法,即便如此,在实际编码中难免会遇到 ...
- Android Paint应用之自定义View实现进度条控件
在上一篇文章<Android神笔之Paint>学习了Paint的基本用法,但是具体的应用我们还没有实践过.从标题中可知,本文是带领读者使用Paint,自定义一个进度条控件. 上图就是本文要 ...
- 【Android 应用开发】自定义View 和 ViewGroup
一. 自定义View介绍 自定义View时, 继承View基类, 并实现其中的一些方法. (1) ~ (2) 方法与构造相关 (3) ~ (5) 方法与组件大小位置相关 (6) ~ (9) 方法与触摸 ...
- Android开发-将自定义View布局到Layout中并调用
写程序的时候,关于布局方面遇到并解决的问题 1.自定义View及其layout属性. 自定义View: [java] view plaincopy public class DrawView exte ...
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
转载请注明地址:http://blog.csdn.net/xiaanming/article/details/10298163 很多的时候,系统自带的View满足不了我们功能的需求,那么我们就需要自己 ...
- Android 自定义 圆环,Android自定义view实现圆环效果实例代码
先上效果图,如果大家感觉不错,请参考实现代码. 重要的是如何实现自定义的view效果 (1)创建类,继承view,重写onDraw和onMesure方法 public class CirclePerc ...
最新文章
- 基于二代和三代测序技术的柚子基因组混合拼装
- 工业组态领头羊--组态王开始涉足.net程序开发(与林伟先生一次近距离接触)...
- window.postMessage跨文档通信
- 【数字信号处理】傅里叶变换性质 ( 傅里叶变换频移性质示例 | PCM 音频信号处理 | 使用 matlab 进行频移操作 )
- 2013豆瓣校园招聘研发类笔试题
- arp攻击源代码(收集)
- Audio Offload
- “Transaction rolled back because it has been marked as rollback-only”
- C#设计模式:迭代器模式(Iterator Pattern)
- html 禁用自动跳转,javascript 实现页面跳转,禁止返回上一页【转】
- 怎么查看raid0或者raid5_海康监控磁盘阵列怎么配置?一文了解清楚
- 平方根的计算(二分逼近、牛顿拉普生法)
- Linux Shell 中 ()、(())、[]、[[]]、{} 的作用
- Linux驱动设置log打印开关
- php 写博客教程,最近写了一个博客程序: QuickBlog PHP 开源的一文多发系统
- python arp 网关_python arp欺骗伪造网关代码
- labwindows制作特殊图形面板
- python进阶day13
- php汉字转拼音百家姓版,百家姓全文查询,百家姓全文带拼音
- 他人——2015实习招聘经验
热门文章
- 郭金东旗下利德东方举行2019年综合应急救援演练
- 进销存软件解决采购审批流程复杂的四大法宝
- Mediocre String Problem (2018南京M,回文+LCP 3×3=9种做法 %%%千年好题 感谢Grunt大佬的细心讲解)...
- css图片自适应裁剪
- PRUNING-卷积神经网络剪枝
- MLK | 机器学习常见算法优缺点了解一下
- Newcoder和LeetCode七月刷题笔记
- CoreData数据库
- css 块级元素与内联元素
- SynchroTrap:基于相似度的异常检测算法