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下拉刷新和加载更多的实现,我想网上一搜就一堆.不过我就没发现比较实用的,要不就是实现起来太复杂,要不就 ...
随机推荐
- [暑假集训--数论]poj1061 青蛙的约会
Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事 ...
- Flex布局--必然的选择
这篇文章是我在阮一峰老师的flex布局教程下,按照自己的理解重写写一遍,以便增强理解.如果你来到这里最好去看一下阮一峰大神的Flex布局教程 正式开始自己的. 说起布局方式,大家首先要了解css3有哪 ...
- 30+ Excellent Windows Phone 7 Development Tutorials
原文发布时间为:2012-01-16 -- 来源于本人的百度文章 [由搬家工具导入] Here are 30+ cool Windows Phone Development articles for ...
- 享元模式FlyweightPattern(转)
解释一下概念:也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象.比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象 ...
- 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---7
以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:
- Color.FromArgb()方法详解
关于颜色值的表示 常用的颜色值表示方式有两种,一种是10进制的RGB值表示,如(0,113,255),三个值分别表示(红,绿,蓝):一种是16进制的颜色码表示,如#ff3212.这两种形式在编程中都可 ...
- java 仓库maven
工具: apache-maven-3.2.3.zip maven_data.zip 在java Window->Preferences->Maven中 Installations中添加ap ...
- Android Studio中删除所有的断点
直接上图,我相信你们能看的懂,骚年们. 第1个图:Run–>>View Breakpoints.. 第2个图:现在可以看到所有的断点了,在左侧栏全选,然后点"-",就全 ...
- 洛谷 P1563 玩具谜题【模拟/环】
题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业. 有一天, 这些玩具小人把小南的眼镜藏了起来. 小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外.如下图: 这时singer告诉 ...
- 基于 OpenResty 的动态服务路由方案
2019 年 5 月 11 日,OpenResty 社区联合又拍云,举办 OpenResty × Open Talk 全国巡回沙龙武汉站,又拍云首席布道师在活动上做了< 基于 OpenResty ...