Android自定义签名View (Kotlin)
好久没有写博客了,今天再次“提笔”回顾一下安卓的自定义view,同时也可以复习一下Kotlin的用法。依然放在首位的必须是效果图:
这字体有木有很高端大气上档次的感觉,写完以后发现我的字体别具一格啊,不做书法家都有点屈才(打住),效果图奉上以后就是我们代码的实现了。最基本的画笔(paint),画布(canvas)是要有的,没什么可详细说的,代码里面每行都有注释简单明了,最重要的是可以直接复制黏贴使用:
package com.sungu.refreshgridviewtest import android.content.Context import android.graphics.* import android.util.AttributeSet import android.util.Base64 import android.view.MotionEvent import android.view.View import java.io.* /** * Created by bobo on 2018/5/16. */ class SignView: View{ lateinit var mPathPaint:Paint //画笔 lateinit var cavas :Canvas //画布 lateinit var path : Path //路径 var mPreX : Float = 0.0f //辅助点x方向坐标 var mPreY : Float =0.0f //辅助点y方向坐标 //一个参数的构造函数 constructor(context: Context):super(context){ init(context) } //两个参数的构造函数(切记他继承的也要是两个函数的构造,否则的话,嘿嘿你懂的) constructor(context: Context,attributeSet: AttributeSet):super(context,attributeSet){ init(context,attributeSet) } //实例化画笔的一些属性值 fun init(context: Context,attributeSet: AttributeSet?=null){ mPathPaint = Paint(Paint.ANTI_ALIAS_FLAG) //绘制时抗锯齿 mPathPaint.isAntiAlias=true //也是辅助抗锯齿(没感觉出效果来) mPathPaint.isDither = true //设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰 mPathPaint.style = Paint.Style.STROKE //画笔的样式 mPathPaint.strokeWidth = 10F //画笔的粗细程度 mPathPaint.color = Color.parseColor("#ff3f4a57") //画笔的颜色 mPathPaint.strokeJoin = Paint.Join.ROUND //线段结束处的形状 mPathPaint.strokeCap = Paint.Cap.ROUND //线段开始结束处的形状 } //测量的方法 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) resetSign() } //重置的方法 private fun resetSign(){ path = Path() cavas= Canvas() } //开始画路径的方法 override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) if (canvas != null) { canvas.drawPath(path, mPathPaint) } } //最关键的touch事件监听 override fun onTouchEvent(event: MotionEvent): Boolean { when (event.getAction()) { MotionEvent.ACTION_DOWN -> { mPreX = event.getX() mPreY = event.getY() path.moveTo(mPreX, mPreY) } MotionEvent.ACTION_MOVE -> { val x = event.getX() val y = event.getY() //绘制圆滑曲线(贝塞尔曲线) path.quadTo(mPreX, mPreY, x, y) mPreX = x mPreY = y } MotionEvent.ACTION_UP -> { path.lineTo(event.getX(), event.getY()) cavas.drawPath(path, mPathPaint) } } //更新View invalidate() return true } /** * 保存bitmap到文件 * * @param fileapth * @return 成功返回true,反之false */ fun SaveBitmapToFile(fileapth: String): Boolean { val file = File(fileapth) if (file.exists()) { val bitmap=getBitMap() var fos: FileOutputStream? = null try { fos = FileOutputStream(file) bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos) return true } catch (e: FileNotFoundException) { e.printStackTrace() return false } finally { try { fos!!.close() } catch (e: IOException) { e.printStackTrace() } } } else return false } fun reset() { resetSign() invalidate() } /** * 获取BitMap图片 * */ fun getBitMap():Bitmap{ // 创建一个bitmap 根据我们自定义view的大小 val returnedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) // 绑定canvas val canvas = Canvas(returnedBitmap) // 获取视图的背景 val bgDrawable = background if (bgDrawable != null) // 如果有就绘制 bgDrawable.draw(canvas) else // 没有就绘制白色 canvas.drawColor(Color.WHITE) // 绘制 draw(canvas) return returnedBitmap } companion object { /** * 保存为base64形式 * */ fun saveBitMapToBase64(bitmap : Bitmap) : String{ var result : String= null.toString() var baos: ByteArrayOutputStream? = null try { if (bitmap != null) { baos = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos) baos!!.flush() baos!!.close() val bitmapBytes = baos!!.toByteArray() result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT) } } catch (e: IOException) { e.printStackTrace() } finally { try { if (baos != null) { baos!!.flush() baos!!.close() } } catch (e: IOException) { e.printStackTrace() } } return result } /** * 将base64形式转换为图片 * */ fun base64ToBitMap(base64 : String) : Bitmap{ val bytes = Base64.decode(base64, Base64.DEFAULT) return BitmapFactory.decodeByteArray(bytes, 0, bytes.size) } } }
直接使用的xml布局:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_ok" android:layout_width="0dp" android:layout_height="50dp" android:gravity="center" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toLeftOf="@+id/btn_reset" android:text="完成"/> <Button android:id="@+id/btn_reset" android:layout_width="0dp" android:layout_height="50dp" android:gravity="center" app:layout_constraintLeft_toRightOf="@+id/btn_ok" app:layout_constraintRight_toLeftOf="@+id/btn_cancel" app:layout_constraintBottom_toBottomOf="parent" android:text="重置"/> <Button android:id="@+id/btn_cancel" android:layout_width="0dp" android:layout_height="50dp" android:gravity="center" app:layout_constraintLeft_toRightOf="@+id/btn_reset" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:text="取消"/> <com.sungu.refreshgridviewtest.SignView android:id="@+id/zdy_signView" android:layout_width="match_parent" android:layout_height="400dp" android:background="#CC9933" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@+id/btn_ok" /> </android.support.constraint.ConstraintLayout>
然后就是控制器activity的代码了
import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.view.View import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity(),View.OnClickListener{ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) btn_ok.setOnClickListener(this) btn_reset.setOnClickListener(this) btn_cancel.setOnClickListener(this) } override fun onClick(p0: View) { when(p0.id){ R.id.btn_ok -> { var reource=SignView.saveBitMapToBase64(zdy_signView.getBitMap()) val intent=Intent() intent.setClass(this@MainActivity,TestImageActivity::class.java) intent.putExtra(TestImageActivity.CONSTANT,reource) startActivity(intent) } R.id.btn_reset -> { zdy_signView.reset() } R.id.btn_cancel -> { finish() } } } }
我这里直接用的base64的方式传递(项目需要),接下来就是将保存的图片显示出来代码:
import android.os.Bundle import android.support.v7.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_test.* /** * Created by bobo on 2018/5/16. */ class TestImageActivity : AppCompatActivity() { //静态属性 companion object { val CONSTANT : String = "RESOUCE" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_test) initIntent() } fun initIntent(){ var result=intent.getStringExtra(CONSTANT) iv_show.setImageBitmap(SignView.base64ToBitMap(result)) } }大功告成。所有的和自定义签名view的代码都在这儿了,欢迎使用,如有问题自己负责(呵呵,评论出来一起改进)。