Android 自己定义ImageView实现圆角/圆形 附加OnTouchListener具体凝视以及Button圆角
转载请注明出处:王亟亟的大牛之路
平时要用一些非方方正正的button之类的小伙伴们是怎样实现的?RadioButton?
ImageButton?
还是其它?
今天亟亟上的是ImageView来实现的
先上下效果图(文件夹结构)
分析:
shape.xml用于Button的”倒角”(做过机械类的都懂,哈哈)
attr.xml用于自己定义ImageView的标签的定义
ids.xml用于控件findbyid用,为什么补+id 等会我会来解释
效果图:
分析:一个Button 2个自己定义ImageView然后 这 3个东西都能够拖拽啊,点击啊等操作,我们来分析下代码。
先从圆角Button開始:
<Button
android:id="@+id/touch_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="快来按我"
android:background="@drawable/shape"/>
没什么特别的差别。仅仅是由于@drawable/shape使得他的样式产生了变化
shape.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 填充的颜色 -->
<solid android:color="#00FF00" />
<!-- 设置button的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="25dip" />
<!-- padding:Button里面的文字与Button边界的间隔 -->
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp"
/>
</shape>
Button就简单的实现了
CycleImageView
public class CycleImageView extends ImageView
{
private Paint mPaint;
private Xfermode mXfermode = new PorterDuffXfermode(Mode.DST_IN);
private Bitmap mMaskBitmap;
private WeakReference<Bitmap> mWeakBitmap;
/**
* 图片的类型,圆形or圆角
*/
private int type;
public static final int TYPE_CIRCLE = 0;
public static final int TYPE_ROUND = 1;
/**
* 圆角大小的默认值
*/
private static final int BODER_RADIUS_DEFAULT = 10;
/**
* 圆角的大小
*/
private int mBorderRadius;
public CycleImageView(Context context)
{
this(context,null);
this.setOnTouchListener(new CustomOnTouchOnGesture());
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
public CycleImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
this.setOnTouchListener(new CustomOnTouchOnGesture());
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CycleImageView);
mBorderRadius = a.getDimensionPixelSize(
R.styleable.CycleImageView_borderRadius, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
BODER_RADIUS_DEFAULT, getResources()
.getDisplayMetrics()));// 默觉得10dp
Log.e("TAG", mBorderRadius+"");
type = a.getInt(R.styleable.CycleImageView_type, TYPE_CIRCLE);// 默觉得Circle
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 假设类型是圆形。则强制改变view的宽高一致,以小值为准
*/
if (type == TYPE_CIRCLE)
{
int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(width, width);
}
}
//清缓存
@Override
public void invalidate()
{
mWeakBitmap = null;
if (mMaskBitmap != null)
{
mMaskBitmap.recycle();
mMaskBitmap = null;
}
super.invalidate();
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas)
{
//在缓存中取出bitmap
Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
if (null == bitmap || bitmap.isRecycled())
{
//拿到Drawable
Drawable drawable = getDrawable();
//获取drawable的宽和高
int dWidth = drawable.getIntrinsicWidth();
int dHeight = drawable.getIntrinsicHeight();
if (drawable != null)
{
//创建bitmap
bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Config.ARGB_8888);
float scale = 1.0f;
//创建画布
Canvas drawCanvas = new Canvas(bitmap);
//依照bitmap的宽高。以及view的宽高,计算缩放比例。由于设置的src宽高比例可能和imageview的宽高比例不同,这里我们不希望图片失真。
if (type == TYPE_ROUND)
{
// 假设图片的宽或者高与view的宽高不匹配。计算出须要缩放的比例。缩放后的图片的宽高。一定要大于我们view的宽高;所以我们这里取大值;
scale = Math.max(getWidth() * 1.0f / dWidth, getHeight()
* 1.0f / dHeight);
} else
{
scale = getWidth() * 1.0F / Math.min(dWidth, dHeight);
}
//依据缩放比例,设置bounds,相当于缩放图片了
drawable.setBounds(0, 0, (int) (scale * dWidth),
(int) (scale * dHeight));
drawable.draw(drawCanvas);
if (mMaskBitmap == null || mMaskBitmap.isRecycled())
{
mMaskBitmap = getBitmap();
}
// Draw Bitmap.
mPaint.reset();
mPaint.setFilterBitmap(false);
mPaint.setXfermode(mXfermode);
//绘制形状
drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint);
mPaint.setXfermode(null);
//将准备好的bitmap绘制出来
canvas.drawBitmap(bitmap, 0, 0, null);
//bitmap缓存起来。避免每次调用onDraw,分配内存
mWeakBitmap = new WeakReference<Bitmap>(bitmap);
}
}
//假设bitmap还存在,则直接绘制就可以
if (bitmap != null)
{
mPaint.setXfermode(null);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
return;
}
}
/**
* 绘制形状
* @return
*/
public Bitmap getBitmap()
{
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
if (type == TYPE_ROUND)
{
canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()),
mBorderRadius, mBorderRadius, paint);
} else
{
canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2,
paint);
}
return bitmap;
}
class CustomOnTouchOnGesture implements OnTouchListener, OnGestureListener {
GestureDetector myGesture = new GestureDetector(getContext(),this);
View view = null;
int[] temp = new int[] { 0, 0 };
@Override
public boolean onTouch(View v, MotionEvent event) {
//这一步仅仅是我的强迫症而已,由于onTouch事件是不断被调用的
if(view == null)
view = v;
myGesture.onTouchEvent(event);
if(event.getAction()==MotionEvent.ACTION_UP){
Log.d("onTouch", "getRawX: "+event.getRawX()+" getRawY: "+event.getRawY());
}
return true;
}
//在按下时调用
@Override
public boolean onDown(MotionEvent e) {
temp[0] = (int) e.getX();
temp[1] = ((int) e.getRawY()) - view.getTop();
return false;
}
//手指在触摸屏上迅速移动,并松开的动作。
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
//长按的时候调用
@Override
public void onLongPress(MotionEvent e) {
Toast.makeText(getContext(), "你长按了麦麦", Toast.LENGTH_LONG).show();
}
//按住然后滑动时调用
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
int x = (int) e2.getRawX();
int y = (int) e2.getRawY();
//设置试图所处的位置
view.layout(x - temp[0], y - temp[1], x + view.getWidth() - temp[0], y - temp[1] + view.getHeight());
return false;
}
// 用户轻触触摸屏。尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
// 注意和onDown()的差别,强调的是没有松开或者拖动的状态
@Override
public void onShowPress(MotionEvent e) {
}
// 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
@Override
public boolean onSingleTapUp(MotionEvent e) {
Toast.makeText(getContext(), "你点击了button", Toast.LENGTH_LONG).show();
return false;
}
}
}
主Activity
public class MainActivity extends Activity {
private Button touchButton;
CycleImageView maimaicircle;
CycleImageView maimairound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touchButton = (Button) findViewById(R.id.touch_button);
maimaicircle=(CycleImageView)findViewById(R.id.maimaicircle);
maimairound=(CycleImageView)findViewById(R.id.maimairound);
maimairound.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
touchButton.setOnTouchListener(new CustomOnTouchOnGesture());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
class CustomOnTouch implements OnTouchListener{
int[] temp = new int[] { 0, 0 };
Boolean ismove = false;
int downX = 0;
int downY = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
int eventaction = event.getAction();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (eventaction) {
case MotionEvent.ACTION_DOWN: // touch down so check if the
temp[0] = (int) event.getX();
temp[1] = y - v.getTop();
downX = (int) event.getRawX();
downY = (int) event.getRawY();
ismove = false;
break;
case MotionEvent.ACTION_MOVE: // touch drag with the ball
v.layout(x - temp[0], y - temp[1], x + v.getWidth() - temp[0], y - temp[1] + v.getHeight());
if (Math.abs(downX - x) > 10 || Math.abs(downY - y) > 10)
ismove = true;
break;
case MotionEvent.ACTION_UP:
if (!ismove)
Toast.makeText(MainActivity.this, "你点击了这个button!!
。!
!。。。。!!
", Toast.LENGTH_LONG).show();
Log.d("MotionEvent.ACTION_UP", "getRawX"+event.getRawX()+" getRawY"+event.getRawY());
break;
}
return false;
}
}
/*
* getRawX:触摸点相对于屏幕的坐标
getX: 触摸点相对于view的坐标
getTop: button左上角相对于父view(LinerLayout)的y坐标
getLeft: button左上角相对于父view(LinerLayout)的x坐标
* */
class CustomOnTouchOnGesture implements OnTouchListener, OnGestureListener {
GestureDetector myGesture = new GestureDetector(MainActivity.this,this);
View view = null;
int[] value = new int[] { 0, 0 };
@Override
public boolean onTouch(View v, MotionEvent event) {
//这一步仅仅是我的强迫症而已,由于onTouch事件是不断被调用的
if(view == null)
view = v;
myGesture.onTouchEvent(event);
if(event.getAction()==MotionEvent.ACTION_UP){
Log.d("onTouch", "getRawX: "+event.getRawX()+" getRawY: "+event.getRawY());
}
return false;
}
//在按下时调用
@Override
public boolean onDown(MotionEvent e) {
value[0] = (int) e.getX();
value[1] = ((int) e.getRawY()) - view.getTop();
return false;
}
//手指在触摸屏上迅速移动。并松开的动作。
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
//长按的时候调用
@Override
public void onLongPress(MotionEvent e) {
Toast.makeText(MainActivity.this, "你长按了button", Toast.LENGTH_LONG).show();
}
//按住然后滑动时调用
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
int x = (int) e2.getRawX();
int y = (int) e2.getRawY();
view.layout(x - value[0], y - value[1], x + view.getWidth() - value[0], y - value[1] + view.getHeight());
return false;
}
// 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
// 注意和onDown()的差别,强调的是没有松开或者拖动的状态
@Override
public void onShowPress(MotionEvent e) {
}
// 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
@Override
public boolean onSingleTapUp(MotionEvent e) {
Toast.makeText(MainActivity.this,"麦麦的位置是"+getMaiMaiLocal(), Toast.LENGTH_LONG).show();
return false;
}
}
public String getMaiMaiLocal(){
int[] location = new int[2];
maimaicircle.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
return "图片各个角Left:"+maimaicircle.getLeft()+"Right:"+maimaicircle.getRight()+"Top:"+maimaicircle.getTop()+"Bottom:"+maimaicircle.getBottom();
}
}
主XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:wjj="http://schemas.android.com/apk/res/com.wjj.ontouchdemo"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.wjj.ontouchdemo.Activity.MainActivity" >
<com.wjj.ontouchdemo.CustomView.CycleImageView
android:id="@id/maimairound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon3"
wjj:type="round"
wjj:borderRadius="20dp">
</com.wjj.ontouchdemo.CustomView.CycleImageView>
<Button
android:id="@+id/touch_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="快来按我"
android:background="@drawable/shape"/>
<com.wjj.ontouchdemo.CustomView.CycleImageView
android:id="@id/maimaicircle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:src="@drawable/icon3"
wjj:type="circle" />
</RelativeLayout>
分析:
在做的过程中发如今XML文件里配置好的+id却在Activity中无法获取到他的Id除非自己在R文件里自己加入,为了避免麻烦才有了ids.xml。
全部的触屏的那些操作已经写在了自己定义的ImageView里了,所以没像Button一样在MainActivity中使用内部类来操作。
具体的OnTouchListener, OnGestureListener周期的凝视已经写在上面了。
大体内容就是如此。
那么这样一个图能做什么?
发散思维:
1.相似于加速球那一类的操作都能够
2.应对于特殊的形状要求
源代码地址:http://yunpan.cn/cdRPzseyfw4rr 訪问password deef
创作的过程中有一部分代码參照了网上的样例,如有雷同。请见谅。
Android 自己定义ImageView实现圆角/圆形 附加OnTouchListener具体凝视以及Button圆角的更多相关文章
- Android自定义ImageView实现图片圆形 ,椭圆和矩形圆角显示
Android中的ImageView只能显示矩形的图片,为了用户体验更多,Android实现圆角矩形,圆形或者椭圆等图形,一般通过自定义ImageView来实现,首先获取到图片的Bitmap,然后通过 ...
- Android自己定义圆角ImageView
我们常常看到一些app中能够显示圆角图片.比方qq的联系人图标等等,实现圆角图片一种办法是直接使用圆角图片资源,当然假设没有圆角图片资源.我们也能够自己通过程序实现的,以下介绍一个自己定义圆角Imag ...
- Android自己定义圆角ImageView 支持网络图片
先看下效果图 我们再来看一张CSDN的圆角图片 从布局能够看出csdn app 的头像也是圆角的Image,但能够看到.有明显的毛刺感.不知道是csdn 程序猿的疏忽还是 我手机的问题,本人手机(小米 ...
- [Android] 给图像加入相框、圆形圆角显示图片、图像合成知识
前一篇文章讲述了Android触屏setOnTouchListener实现突破缩放.移动.绘制和加入水印,继续我的"随手拍"项目完毕给图片加入相框.圆形圆角显示图片和图像合 ...
- Android ImageView加载圆形图片且同时绘制圆形图片的外部边缘边线及边框
Android ImageView加载圆形图片且同时绘制圆形图片的外部边缘边线及边框 在Android早期的开发中,如果涉及到圆形图片的处理,往往需要借助于第三方的实现,见附录文章1,2.And ...
- android 自己定义水平和圆形progressbar 仅仅定义一些style就能够
效果图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/diss ...
- Android 自己定义UI圆角button
Android实际开发中我们一般须要圆角的button,普通情况下我们能够让美工做出来对应的button图片.然后放上去就可以,另外我们能够在布局文件里直接设置,也能够达到一样的效果. 以下解说在布局 ...
- Android学习笔记之如何使用圆形菜单实现旋转效果...
PS:最近忙于项目的开发,一直都没有去写博客,是时候整理整理自己在其中学到的东西了... 学习内容: 1.使用圆形菜单并实现旋转效果.. Android的圆形菜单我也是最近才接触到,由于在界面 ...
- Android UI--自定义ListView(实现下拉刷新+加载更多)
Android UI--自定义ListView(实现下拉刷新+加载更多) 关于实现ListView下拉刷新和加载更多的实现,我想网上一搜就一堆.不过我就没发现比较实用的,要不就是实现起来太复杂,要不就 ...
随机推荐
- Python(2)-- 运算符
1. 算术运算符 常规: 加(+).减(-).乘(*).除(/).取模(%) 此外: 幂(**):返回x的y次幂, eg: 2**3---返回 2 的 5 次幂,输出结果32 取整除(//):返回商的 ...
- 【02】 Vue 之 数据绑定
2.1. 什么是双向绑定? Vue框架很核心的功能就是双向的数据绑定. 双向是指:HTML标签数据 绑定到 Vue对象,另外反方向数据也是绑定的.通俗点说就是,Vue对象的改变会直接影响到HTML的标 ...
- 运输问题2(cogs 12)
[问题描述] 一个工厂每天生产若干商品,需运输到销售部门进行销售.从产地到销地要经过某些城镇,有不同的路线可以行走,每条两城镇间的公路都有一定的流量限制.为了保证公路的运营效率,每条公路都有一 ...
- MVP MVVM MVC
上一篇得到大家的关注,非常感谢.由于自己对于这些模式的理解也是有限,对于MVC,MVP,MVVM这些模式的比较,是结合自己的理解,一些地方不一定准确,需要的朋友可以参考下 上一篇得到大家的关注,非常感 ...
- 自己写的页面加载进度条jquery插件
(function ($) { var progressId = "progress" + Math.round(Math.random() * 100) var progress ...
- linux下解压zip文件
linux下解压zip文件 linux自带的unzip命令可以解压windows下的zip格式的压缩文件. unzip命令 语法:unzip [选项] 压缩文件名.zip 各选项的含义分别为: -x ...
- 腾讯云使用liveRoom开启直播时,报“房间已存在”错误?
利用腾讯云roomService服务,移动直播,创建房间api,CreateRoom时有时报“房间已存在”错误. 分析流程发现,CreateRoom会传入roomId到roomService后台,后台 ...
- (22)python PhantomJS
下载地址 https://bitbucket.org/ariya/phantomjs/downloads/ 安装 解压后把bin目录下的phantomjs.exe文件复制到C:\Python27\Sc ...
- Linux shell 环境变量及有效范围
每当我们使用ssh客户端远程登陆一个服务时,操作系统就会给我们分配一个新的shell,并且这个shell继承了操作系统的永久环境变量.在当前的shell执行一个sh文件,都会临时产生一个子shell, ...
- 笔记-迎难而上之Java基础进阶8
函数式接口 函数式接口:有且只有一个抽象方法的接口,(可以包含其他默认,静态,私有方法) //函数式接口的使用,创建一个函数式接口 public interface FunctionalInterfa ...