Android自定义控件---“取消”视图

一、前言

好久没更新博客了,最近一直在忙,找实习,整理毕业资料,找房子等等。现在大部分基本忙完了,这几天在公司写的一些自定义控件,打算发出来与大家分享。

首先看看最终的效果图

这个自定义视图的应用场景,主要用于替代对话框左上角或者右上角的取消按钮。按照我以前的做法,都是设计师发给我一张图片,我自己创建一个ImageView,再把图片设置进去就结束了。如果有天产品经理看得不爽,想要圆形,或矩形,对角线想要XXX颜色。又得等设计师发图了(本人不太会PS啊)。所以为了避免麻烦,打算自己写一个。

二、自定义控件个人思路

创建了一个CustomCancelView类,直接继承View。重写里面的onMeasure和onDraw方法。

1、重写onMeasure方法,强制将视图设置为宽度和高度相等。在Xml文件中只写layout_width属性值就可以了,layout_height属性的值随便写。

@Override
 protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
}

2、接下来比较麻烦的就是重写onDraw方法。

在onDraw方法里面画一个圆形,矩形,圆矩形是不难的。

那么要思考的问题就是怎么画对角线,并且这个对角线的大小要随着该视图的大小改变而改变。

我以画一个圆形的左对角线为例子。

(1)首先我们假设这个矩形的黑色边框就是我们控件的视图边界,设它的宽度width和高度height为100(这个黑色边框我们不可见。)

(2) 再画一个圆形,这个蓝色的圆形就是我们可见的视图。

(3) 获取圆形的半径,半径为circleRadius=width/2。即半径50(图中的三条线为圆的半径)

(4) 再画一条红色的垂直线,由于1,2,3边构成的三角形很明显就是一个等腰三角形嘛(哎,我数学不太好。),设1,2边为X,3边的值,我们知道是50(这是半径值啊)。根据勾股定理解出X的值约等于35。也就是1,2边的长度为35。

程序中的代码为int rectangleHalf=(int)Math.sqrt((circleRadius*circleRadius)/2);

(5)接下来再计算图中绿色点的坐标,设坐标为(leftDiagonalStartX,leftDiagonalStartY)。则

x=半径值50-2边的值(35)=15

y=半径值50-1边的值(35)=15。

所以这个绿色点坐标为(15,15)

程序代码

leftDiagonalStartX = leftDiagonalStartY = circleRadius - rectangleHalf

(6)设这个右下角的绿色点坐标为(leftDiagonalEndX ,leftDiagonalEndY)。则

x=半径值50+5边的值(35)=85

y=半径值50+6边的值(35)=85。

所以这个绿色点坐标为(85,85)

程序代码

leftDiagonalEndX = leftDiagonalEndY = circleRadius + rectangleHalf

根据数学定义:两点确定一条直线。

最后调用canvas.drawLine()绘制这条左对角线出来

关于右对角线的绘制就不说了,基本思路和上面讲的差不多。

关于onDraw方法的核心代码如下

@Override
 protected void onDraw(Canvas canvas) {
    //计算圆的半径
    circleRadius = getWidth() / 2;

    ////绘制正方形/圆形/圆矩形(注意绘制此图形的目的是用来作为背景)。
    drawView(canvas , paintTemp) ;

    //得到圆内接矩形的长度的一半
    int rectangleHalf = (int) Math. sqrt(( circleRadius * circleRadius ) / 2) ;

    //左对角线的坐标
    int leftDiagonalStartX, leftDiagonalStartY , leftDiagonalEndX, leftDiagonalEndY;

    //计算左对角线的坐标(需要考虑paintWidth和diagonalLength对对角线的影响,觉得这两个参数影响代码阅读可以暂时去掉)
    leftDiagonalStartX = leftDiagonalStartY = circleRadius - rectangleHalf + paintWidth + diagonalLength ;
    leftDiagonalEndX = leftDiagonalEndY = circleRadius + rectangleHalf - paintWidth - diagonalLength;

    //绘制左对角线
    drawDiagonal(canvas , leftDiagonalStartX, leftDiagonalStartY, leftDiagonalEndX , leftDiagonalEndY, diagonalLeftColor , paintCapRound) ;

    //右对角线的坐标
    int rightDiagonalStartX, rightDiagonalStartY , rightDiagonalEndX, rightDigonalEndY;
    //计算右对角线的坐标(需要考虑paintWidth和diagonalLength对对角线的影响,觉得这两个参数影响代码阅读可以暂时去掉)
    rightDiagonalStartX = circleRadius + rectangleHalf - paintWidth - diagonalLength;
    rightDiagonalStartY = circleRadius - rectangleHalf + paintWidth + diagonalLength;
    rightDiagonalEndX = circleRadius - rectangleHalf + paintWidth + diagonalLength;
    rightDigonalEndY = circleRadius + rectangleHalf - paintWidth - diagonalLength;
    //绘制右对角线
    drawDiagonal(canvas , rightDiagonalStartX, rightDiagonalStartY, rightDiagonalEndX, rightDigonalEndY , diagonalRightColor, paintCapRound);

    //绘制正方形/圆形/圆矩形
    drawView(canvas , mPaint) ;
}

/**
* 绘制视图
*
* @param canvas
 * @param paint
 */
public void drawView(Canvas canvas , Paint paint) {
    if (cancelViewType == SQUARE) {
        canvas.drawRect(paintWidth , paintWidth, getWidth() - paintWidth, getHeight() - paintWidth, paint) ;
    } else if (cancelViewType == CIRCLE) {
        canvas.drawCircle(circleRadius , circleRadius, circleRadius - paintWidth , paint);
    } else if (cancelViewType == ROUNDED_RECTANGLE) {
        canvas.drawRoundRect(new RectF(paintWidth, paintWidth, getWidth() - paintWidth, getHeight() - paintWidth), roundDegree, roundDegree , paint);
    } else {
        Log.e( "------------->", "视图类型错误!" );
    }
}

/**
* 绘制对角线
*
* @param canvas
 * @param diagonalStartX
 * @param diagonalStartY
 * @param diagonalEndX
 * @param diagonalEndY
 */
public void drawDiagonal(Canvas canvas , int diagonalStartX, int diagonalStartY, int diagonalEndX, int diagonalEndY , int diagonalColor, boolean paintCapRound) {
    Paint diagonalPaint = new Paint();
    diagonalPaint.setAntiAlias( true);
    diagonalPaint.setColor(diagonalColor) ;
    diagonalPaint.setStrokeWidth( diagonalPaintWidth);
    diagonalPaint.setStyle(Paint.Style. STROKE);
    if (paintCapRound ) {
        //设置画笔的半圆风格
        diagonalPaint.setStrokeCap(Paint.Cap. ROUND);
    } else {
        //设置画笔默认值
        diagonalPaint.setStrokeCap(Paint.Cap. SQUARE);
    }
    //绘制对角线
    canvas.drawLine(diagonalStartX , diagonalStartY, diagonalEndX, diagonalEndY , diagonalPaint);
}

(7)XML文件布局

注意在XML文件一定要添加这句xmlns:app="http://schemas.android.com/apk/res-auto"(其中xmlns:=xxx这里的名字可以自定义)

<? xml version="1.0" encoding= "utf-8"?>
<LinearLayout xmlns: android="http://schemas.android.com/apk/res/android"
    xmlns: app="http://schemas.android.com/apk/res-auto"
    xmlns: tools="http://schemas.android.com/tools"
    android :layout_width="match_parent"
    android :layout_height="match_parent"
    android :background="#d4d4d4"
    android :orientation="vertical">

    <TextView
        android :layout_width="match_parent"
        android :layout_height="wrap_content"
        android :padding="10dp"
        android :text="正方形,圆形,圆矩形"
        android :textColor="#000000"
        android :textSize="16dp" />

    <LinearLayout
        android :layout_width="match_parent"
        android :layout_height="wrap_content"
        android :orientation="horizontal">

        <per.edward.ui.CustomCancelView
            android :id="@+id/cancel_one"
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp" />

        <per.edward.ui.CustomCancelView
            android :id="@+id/cancel_two"
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Type="circle" />

        <per.edward.ui.CustomCancelView
            android :id="@+id/cancel_three"
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Round_Degree="35"
            app :CC_Type="rounded_rectangle" />
    </LinearLayout>

    <TextView
        android :layout_width="match_parent"
        android :layout_height="wrap_content"
        android :padding="10dp"
        android :text="正方形(改变了边框的颜色),圆形(改变了背景颜色),圆矩形(改变了对角线的颜色)"
        android :textColor="#000000"
        android :textSize="16dp" />

    <LinearLayout
        android :layout_width="match_parent"
        android :layout_height="wrap_content"
        android :orientation="horizontal">

        <per.edward.ui.CustomCancelView
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Stroke_Width="3dp"
            app :CC_Stroke_Width_Color="#31b2f7" />

        <per.edward.ui.CustomCancelView
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Background="#7ccf66"
            app :CC_Type="circle" />

        <per.edward.ui.CustomCancelView
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Diagonal_Left_Color="#3af700"
            app :CC_Diagonal_Right_Color="#fff200"
            app :CC_Round_Degree="35"
            app :CC_Type="rounded_rectangle" />
    </LinearLayout>

    <TextView
        android :layout_width="match_parent"
        android :layout_height="wrap_content"
        android :padding="10dp"
        android :text="正方形(边框和背景设置为透明),圆形(对角线变宽了,并且对角线末端为圆角),圆矩形(对角线的末端为矩形)"
        android :textColor="#000000"
        android :textSize="16dp" />

    <LinearLayout
        android :layout_width="match_parent"
        android :layout_height="wrap_content"
        android :orientation="horizontal">

        <per.edward.ui.CustomCancelView
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Background="#00000000"
            app :CC_Diagonal_Length ="10dp"
            app :CC_Stroke_Width_Color="#00000000" />

        <per.edward.ui.CustomCancelView
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Background="#e079ead7"
            app :CC_Diagonal_Length ="10dp"
            app :CC_Diagonal_Paint_Width="10dp"
            app :CC_Type="circle" />

        <per.edward.ui.CustomCancelView
            android :layout_width="80dp"
            android :layout_height="80dp"
            android :layout_margin="10dp"
            app :CC_Diagonal_Left_Color="#596dd1"
            app :CC_Diagonal_Length ="10dp"
            app :CC_Diagonal_Paint_Width="10dp"
            app :CC_Diagonal_Right_Color="#bd79c4"
            app :CC_Paint_Cap_Round="false"
            app :CC_Round_Degree="35"
            app :CC_Type="rounded_rectangle" />
    </LinearLayout>
</LinearLayout>

三、结束

分享写完,我搬砖去了。

Demo源码请戳这里:Android自定义控件---“取消”视图

时间: 2024-11-02 21:58:26

Android自定义控件---“取消”视图的相关文章

android自定义控件系列教程----视图的测量和布局

前面说点什么 当我们的一个视图界面绘制在android屏幕上面的时候其实都必须经过这几步measure. layout.draw这几个阶段,我们可以在view类里面看到这几个函数,然后里面有几个函数是onmeasure.onlayout.ondraw这几个函数是我们重写控件需要注意的这几个函数,下面我们就来讲讲这几个函数的功能和作用. onMeasure 正如这个函数的名子一样就是测量,所有的图示其实系统在绘制之前都不知道它到底有多大的,所以在很多时候我们在初始化界面oncreate的时候直接去

android自定义控件系列教程----视图

理解android视图 对于android设备我们所看到的区域其实和它在底层的绘制有着很大的关系,很多时候我们都只关心我们所看到的,那么在底层一点它到底是怎么样的一个东西呢?让我们先来看看这个图. 对于整个设备的可见区域而言其实就是我们中间的那个屏幕,从上面的拿个图可以清晰的看到,除了我们的可见区域在它的上下左右都应该有内容,那么在android系统中是怎么控制显示它的位置呢?下面我们来解答这个问题. android如何控制视图的显示位置 我们可以打开view类的源码找到这两个函数 /** *

自定义控件三部曲视图篇(三)——瀑布流容器WaterFallLayout实现

前言:只要在前行,梦想就不再遥远 系列文章: Android自定义控件三部曲文章索引:http://blog.csdn.net/harvic880925/article/details/50995268 前面两节讲解了有关ViewGroup的onMeasure.onLayout的知识,这节我们深入性地探讨一下,如何实现经常见到的瀑布流容器,本节将实现的效果图如下: 从效果图中可以看出这里要完成的几个功能: 1.图片随机添加 2.在添加图片时,总是将新图片插入到当前最短的列中 3.每个Item后,

android 自定义控件---圆形方向盘

在做Android平台开发的时候,经常会遇到安卓原生控件无法满足需求的情况,安卓允许开发者去继承已经存在的控件或者实现你自己的控件. 先来看一下效果图 采用直接集成View类,重写onDrow方法绘制. 下面附上主要代码. 1 新建一个类CircleView 继承自View 1 package com.lennon.view; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import androi

Android自定义控件_View的绘制流程

每一个View/ViewGroup的显示都会经过三个过程:1.measure过程(测量View显示的大小,位置):2.layout过程(布局view的位置):3.draw过程(上一篇文章说到的通过canvas绘制到界面上显示,形成了各色的View) 下面分析一下各个过程:measure过程: 因为DecorView实际上是派生自FrameLayout的类,也即一个ViewGroup实例,该ViewGroup内部的ContentViews又是一个ViewGroup实例,依次内嵌View或ViewG

【转】Android自定义控件

原文网址:http://blog.163.com/[email protected]/blog/static/103242241201382210910473/ 开发自定义控件的步骤: 1.了解View的工作原理 2. 编写继承自View的子类 3. 为自定义View类增加属性 4. 绘制控件 5. 响应用户消息 6 .自定义回调函数 一.View结构原理 Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类. Vie

Android自定义控件一_Canvas分析

自定义控件分为两种一种是自定义ViewGroup控件,一种是自定义View控件:跟踪View的步伐其实能跟到Java实现的最下面我们能发现的也就只有Canvas了,再下去就是C++或C实现了:所以本文主要是站在设计的的角度讲解一下Canvas跟View的关系,再简单分析一下Canvas用法: View作为Android中一切显示视图的父类,我们可看到它的绘制方法draw(Canvas canvas)中,无非也是通过Canvas的绘制来达到各种View的显示,如此Android中各种控件如:Ima

Android自定义控件之日历控件

Android自定义控件之日历控件 2015-10-23 Android开发中文站 三月份学习android,至今也有半年有余,中间也做过两个项目,但是依然感觉自己做的应用不是很有新意,比不上应用市场上那些应用如此绚丽.所以自己仍需继续努力.学习至今,仍感觉自定义控件是一块硬骨头,还没修炼到身后的内功,下面就切入正题,以一次项目的需求,来实现一个自定义的日历控件.效果图先来一发. 我们分析下效果图,然后确定我们的需求. (1).绘制星期的自定义View,用于标识日期的礼拜. (2).绘制日期的自

[Android自定义控件] Android自定义控件

转载自:http://blog.163.com/[email protected]/blog/static/103242241201382210910473/ 开发自定义控件的步骤: 1.了解View的工作原理 2. 编写继承自View的子类 3. 为自定义View类增加属性 4. 绘制控件 5. 响应用户消息 6 .自定义回调函数 一.View结构原理 Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类. View