Android - 自定义控件之圆形控件
自定义控件 - 圈圈
Android L; Android Studio
效果:能够自定义圆圈半径和位置;设定点击效果;改变背景颜色
下面是demo图
点击前: 点击后:
自定义控件一般要继承View;写出构造方法,并设定属性;复写onDraw
方法
并在xml中配置一下
例子:OptionCircle.java
CirclesActivity.java
activity_circle_choose.xml
这个例子没有使用attrs.xml
控件 OptionCircle
这里继承的是ImageView;设定了多个属性,有半径,圆心位置,背景颜色和字体颜色等等
针对这些属性,开放set方法;方便设置属性;可以改变这些属性来做出一些动画效果
构造方法中预设几个属性,设置画笔,背景颜色和圆圈的半径
onDraw
方法中开始绘制控件;先画圆形,在圆形中心画上文字;文字中心定位需要特别计算一下
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class OptionCircle extends ImageView {
private final Paint paint;
private final Context context;
boolean clicked = false;// 是否被点击
boolean addBackground = false;
int radius = -1; // 半径值初始化为-1
int centerOffsetX = 0;// 圆圈原点的偏移量x
int centerOffsetY = 0;// 偏移量y
int colorCircle; // 圆圈颜色
int colorBackground; // 背景填充颜色
int colorText; // 文字颜色
String textCircle = "";
public OptionCircle(Context context) {
this(context, null);
}
public OptionCircle(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
this.paint = new Paint();
this.paint.setAntiAlias(true);
this.paint.setStyle(Paint.Style.STROKE);
colorCircle = Color.argb(205, 245, 2, 51);// 默认颜色
colorText = colorCircle; // 字体颜色默认与圈圈颜色保持一致
colorBackground = colorCircle;// 设定默认参数
}
// 属性设置方法
public void setRadius(int r) {
this.radius = r;
}
public void setCenterOffset(int x, int y) {
this.centerOffsetX = x;
this.centerOffsetY = y;
}
public void setColorCircle(int c) {
this.colorCircle = c;
}
public void setColorText(int c) {
this.colorText = c;
}
public void setColorBackground(int c) {
this.colorBackground = c;
}
public void setText(String s) {
this.textCircle = s;
}
public void setClicked(boolean clicked) {
this.clicked = clicked;
}
public void setAddBackground(boolean add) {
this.addBackground = add;
}
@Override
protected void onDraw(Canvas canvas) {
int center = getWidth() / 2;// 当前宽度的一半
int innerCircle = 86; // 默认半径为86
if (radius > 0) {
innerCircle = dip2px(context, radius); // 如果没有另外设置半径,取半径86
}
Drawable drawable = getDrawable();
if (addBackground) {
} else {
// 画圈圈;被点击后会变成实心的圈圈,默认是空心的
this.paint.setStyle(clicked ? Paint.Style.FILL : Paint.Style.STROKE);
this.paint.setColor(clicked ? colorBackground : colorCircle);
this.paint.setStrokeWidth(1.5f);
canvas.drawCircle(center + centerOffsetX, center + centerOffsetY,
innerCircle, this.paint);// 画圆圈时带上偏移量
}
// 绘制文字
this.paint.setStyle(Paint.Style.FILL);
this.paint.setStrokeWidth(1);
this.paint.setTextSize(22);
this.paint.setTypeface(Typeface.MONOSPACE);// 设置一系列文字属性
this.paint.setColor(clicked ? Color.WHITE : colorText);
this.paint.setTextAlign(Paint.Align.CENTER);// 文字水平居中
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
canvas.drawText(textCircle, center + centerOffsetX,
center + centerOffsetY - (fontMetrics.top + fontMetrics.bottom) / 2, this.paint);// 设置文字竖直方向居中
super.onDraw(canvas);
}
/**
* convert dp to px
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
配置 activity_circle_choose.xml
控件文件定义完毕,在activity_circle_choose.xml
中配置一下
定义4个圈圈;center_circle定位在中心;circle_0是红色的;circle_1是绿色的;circle_2是洋红色的
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/top_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:text="@string/circles_top_title"
android:textSize="26sp" />
<com.rust.aboutview.view.OptionCircle
android:id="@+id/center_circle"
android:layout_width="140dp"
android:layout_height="140dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />
<com.rust.aboutview.view.OptionCircle
android:id="@+id/circle_0"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginStart="130dp"
android:layout_marginTop="53dp" />
<com.rust.aboutview.view.OptionCircle
android:id="@+id/circle_1"
android:layout_width="210dp"
android:layout_height="210dp"
android:layout_below="@+id/circle_0"
android:layout_toEndOf="@+id/center_circle" />
<com.rust.aboutview.view.OptionCircle
android:id="@+id/circle_2"
android:layout_width="210dp"
android:layout_height="210dp"
android:layout_below="@id/center_circle" />
</RelativeLayout>
在 CirclesActivity.java 中使用圈圈
圈圈类OptionCircle.java
已经开放了设置属性的方法,我们可以利用这些方法来调整圈圈的样式,比如半径,颜色,圆心偏移量
center_circle固定在屏幕中间不动
circle_0仿造一个放大缩小的效果,改变半径值即可实现
circle_1仿造一个浮动的效果,改变圆心偏移量来实现
circle_2仿造抖动效果,也是改变圆心偏移量
这些圈圈都可以自定义背景颜色
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import com.rust.aboutview.view.OptionCircle;
public class CirclesActivity extends Activity {
public static final String TAG = "CirclesActivity";
public static final int circle0_r = 88;
private static final int SLEEPING_PERIOD = 100; // 刷新UI间隔时间
private static final int UPDATE_ALL_CIRCLE = 99;
int circleCenter_r;
int circle1_r;
boolean circle0Clicked = false;
boolean circle1Clicked = false;
OptionCircle centerCircle;
OptionCircle circle0;
OptionCircle circle1;
OptionCircle circle2;
CircleHandler handler = new CircleHandler(this);
/**
* Handler : 用于更新UI
*/
static class CircleHandler extends Handler {
CirclesActivity activity;
boolean zoomDir = true;
boolean circle2Shaking = false;
int r = circle0_r;
int moveDir = 0; // 浮动方向
int circle1_x = 0;// 偏移量的值
int circle1_y = 0;
int circle2_x = 0;
int circle2ShakeTime = 0;
int circle2Offsets[] = {10, 15, -6, 12, 0};// 抖动偏移量坐标
CircleHandler(CirclesActivity a) {
activity = a;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_ALL_CIRCLE: {
if (zoomDir) {// 用简单的办法实现半径变化
r++;
if (r >= 99) zoomDir = false;
} else {
r--;
if (r <= circle0_r) zoomDir = true;
}
activity.circle0.invalidate();
activity.circle0.setRadius(r);
calOffsetX();// 计算圆心偏移量
activity.circle1.invalidate();
activity.circle1.setCenterOffset(circle1_x, circle1_y);
if (circle2Shaking) {
if (circle2ShakeTime < circle2Offsets.length - 1) {
circle2ShakeTime++;
} else {
circle2Shaking = false;
circle2ShakeTime = 0;
}
activity.circle2.invalidate();
activity.circle2.setCenterOffset(circle2Offsets[circle2ShakeTime], 0);
}
}
}
}
// 计算circle1圆心偏移量;共有4个浮动方向
private void calOffsetX() {
if (moveDir == 0) {
circle1_x--;
circle1_y++;
if (circle1_x <= -6) moveDir = 1;
}
if (moveDir == 1) {
circle1_x++;
circle1_y++;
if (circle1_x >= 0) moveDir = 2;
}
if (moveDir == 2) {
circle1_x++;
circle1_y--;
if (circle1_x >= 6) moveDir = 3;
}
if (moveDir == 3) {
circle1_x--;
circle1_y--;
if (circle1_x <= 0) moveDir = 0;
}
}
}
class UpdateCircles implements Runnable {
@Override
public void run() {
while (true) {// 配合Handler,循环刷新UI
Message message = new Message();
message.what = UPDATE_ALL_CIRCLE;
handler.sendEmptyMessage(message.what);
try {
Thread.sleep(SLEEPING_PERIOD); // 暂停
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circle_choose);
centerCircle = (OptionCircle) findViewById(R.id.center_circle);
circle0 = (OptionCircle) findViewById(R.id.circle_0);
circle1 = (OptionCircle) findViewById(R.id.circle_1);
circle2 = (OptionCircle) findViewById(R.id.circle_2);
circleCenter_r = 38;
circle1_r = 45;
// 设置圈圈的属性
centerCircle.setRadius(circleCenter_r);
centerCircle.setColorText(Color.BLUE);
centerCircle.setColorCircle(Color.BLUE);
centerCircle.setText("点击圈圈");
circle0.setColorText(Color.RED);
circle0.setRadius(circle0_r);
circle0.setText("RED");
circle1.setColorCircle(Color.GREEN);
circle1.setColorText(Color.GREEN);
circle1.setText("Green");
circle1.setRadius(circle1_r);
circle2.setColorCircle(getResources().getColor(R.color.colorMagenta));
circle2.setColorText(getResources().getColor(R.color.colorMagenta));
circle2.setText("Frozen!");
// 设定点击事件,可在这里改变控件的属性
circle0.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
circle0Clicked = !circle0Clicked; // 每次点击都取反
circle0.setClicked(circle0Clicked);
}
});
circle1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
circle1Clicked = !circle1Clicked;
circle1.setColorBackground(Color.GREEN);
circle1.setClicked(circle1Clicked);
}
});
circle2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.circle2Shaking = true;// 颤抖吧!
}
});
Thread t = new Thread(new UpdateCircles());
t.start();// 开启子线程
}
}
至此,圈圈demo结束。通过简单的计算,模拟出浮动,抖动,缩放的效果
以上的代码,复制粘贴进工程里就能使用。圆心移动的轨迹,用三角函数来计算会更好
这里继承的是ImageView,应该有办法在圈内动态添加背景Bitmap,效果更好看
Android - 自定义控件之圆形控件的更多相关文章
- Android自定义控件之日历控件
标签: android 控件 日历 应用 需求 2015年09月26日 22:21:54 25062人阅读 评论(109) 收藏 举报 分类: Android自定义控件系列(7) 版权声明:转载注 ...
- Android自定义控件1--自定义控件介绍
Android控件基本介绍 Android本身提供了很多控件比如我们常用的有文本控件TextView和EditText:按钮控件Button和ImageButton状态开关按钮ToggleButton ...
- Android开发技巧——自定义控件之组合控件
Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...
- 自定义圆形控件RoundImageView并认识一下attr.xml
今天我们来讲一下有关自定义控件的问题,今天讲的这篇是从布局自定义开始的,难度不大,一看就明白,估计有的同学或者开发者看了说,这种方式多此一举,但是小编我不这么认为,多一种解决方式,就多一种举一反三的学 ...
- 自定义圆形控件RoundImageView并认识一下attr
昨天我们学习了自定义带图片和文字的ImageTextButton,非常简单,我承诺给大家要讲一下用自定义属性的方式学习真正的实现自定义控件,在布局文件中使用属性的方式就需要用到attr.xml这个文件 ...
- Android常用酷炫控件(开源项目)github地址汇总
转载一个很牛逼的控件收集帖... 第一部分 个性化控件(View) 主要介绍那些不错个性化的 View,包括 ListView.ActionBar.Menu.ViewPager.Gallery.Gri ...
- Android 常用炫酷控件(开源项目)git地址汇总
第一部分 个性化控件(View) 主要介绍那些不错个性化的 View,包括 ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.P ...
- 自定义圆形控件 RoundImageView
1.自定义圆形控件 RoundImageView package com.ronye.CustomView; import android.content.Context; import androi ...
- CircleImageView自定义圆形控件的使用
1.自定义圆形控件github地址: https://github.com/hdodenhof/CircleImageView 主要的类: package de.hdodenhof.circleima ...
随机推荐
- (转)java匿名内部类详解
原文:http://android.blog.51cto.com/268543/384844/ 内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同. *内部类可以是静态static的 ...
- 互联网“剁手”新方向,VR全景购物忙——全景智慧城市常诚
随着VR和AR技术的兴起,各行各业都在寻求VR+的对接方式,除了游戏和社交平台,另一大对VR有着浓厚兴趣的就是电商平台了,阿里.京东等电商巨头纷纷成立VR事业部,如何让亿万用户在VR中愉快的买买买,已 ...
- iOS地理围栏技术的应用
遇到一个需求,要求监测若干区域,设备进入这些区域则要上传数据,且可以后台监测,甚至app被杀死也要监测.发现oc的地理围栏技术完美匹配这个需求,任务做完了,把遇到的坑记录下来,也许能帮到你呢. 要做这 ...
- 基于TF-IDF的新闻标签提取
基于TF-IDF的新闻标签提取 1. 新闻标签 新闻标签是一条新闻的关键字,可以由编辑上传,或者通过机器提取.新闻标签的提取主要用于推荐系统中,所以,提取的准确性影响推荐系统的有效性.同时,对于将标签 ...
- es6之各种数据类型的扩展
一. 字符串的扩展 为字符串添加了Iterator,可以被for...of遍历 includes.startsWith.endsWith都会返回布尔值,且支持第二个参数(开始搜索的位置),endsWi ...
- C#从基于FTPS的FTP server下载数据 (FtpWebRequest 的使用)SSL 加密
FTPS,亦或是FTPES, 是FTP协议的一种扩展,用于对TLS和SSL协议的支持. 本文讲述了如何从一个基于FTPS的Server中下载数据的实例. 任何地方,如有纰漏,欢迎诸位道友指教. ...
- ios移动端原生滚动条滚动不灵敏问题
最近开发微信页面的时候遇到了这个问题, 因为移动端浏览器的原生滚动条样式很好,不像pc端那么难看,所以在页面需要滚动的地方用了原生的滚动条,这种滚动条在安卓浏览器中没有任何问题,但是在ios微信浏览器 ...
- Java基础——继承
学习Java继承之前,我们想回忆一下Java面向对象需要特别注意的几个关键点. 面向对象是将复杂的事情简单化了,它通过封装的方式使得代码的重用性更高和安全性更强.平时我们要学会用面向对象的方式去思考, ...
- lftp的用法
lftp是Linux下的一个ftp工具,支持ftp, ftps, http, https, hftp, fish, sftp, file, bittorrent等协议(支持https 和 ftps,必 ...
- 表连接查询的顺序和where子句条件的前后顺序会影响sql的性能么
有好多时候,我们常听别人说大表在前,小表在后,包括现在好多百度出来的靠前的答案都有说数据库是从右到左加载的,所以from语句最后关联的那张表会先被处理.如果三表交叉,就选择交叉表来作为基础表.等等一些 ...