Android 自定义ImageView实现圆角/圆形 附加OnTouchListener详细注释以及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" />
    <!-- 设置按钮的四个角为弧形 -->
    <!-- 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(), "你点击了按钮", 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, "你点击了这个按钮!!!!!!!!!!!", Toast.LENGTH_LONG).show();
                    Log.d("MotionEvent.ACTION_UP", "getRawX"+event.getRawX()+" getRawY"+event.getRawY());
                break;
            }
            return false;
        }
    }

    /*
     * getRawX:触摸点相对于屏幕的坐标
       getX: 触摸点相对于view的坐标
       getTop: 按钮左上角相对于父view(LinerLayout)的y坐标
       getLeft: 按钮左上角相对于父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, "你长按了按钮", 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 访问密码 deef

创作的过程中有一部分代码参照了网上的例子,如有雷同,请见谅。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-14 04:21:39

Android 自定义ImageView实现圆角/圆形 附加OnTouchListener详细注释以及Button圆角的相关文章

Android自定义ImageView实现图片圆形 ,椭圆和矩形圆角显示

Android中的ImageView只能显示矩形的图片,为了用户体验更多,Android实现圆角矩形,圆形或者椭圆等图形,一般通过自定义ImageView来实现,首先获取到图片的Bitmap,然后通过Paint和onDraw()进行圆形图片显示. 效果图: 代码: 1 activity_image.xml 2 <?xml version="1.0" encoding="utf-8"?> 3 <LinearLayoutxmlns:android=&q

Android -- 自定义ImageView(圆形头像)

1.  原图      -->    2.  自定义的控件类 package com.chaowen.yixin; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Pa

Android自定义ImageView圆形头像

效果图: 代码如下: RoundImageView.java import cn.comnav.evaluationsystem.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import a

Android 自定义ImageView支持缩放,拖拽,方便复用

今天刚发了一篇关于ImageView的缩放和拖拽的博客,然后我想了下,将他自定义下,方便我们来复用这个imageView,效果我就不多说了,http://blog.csdn.net/xiaanming/article/details/8827257就是这个效果,我只是把他抽出来自定义了下,代码还是贴上吧,我也将demo上传一下,有疑问大家指出来,大家共同学习,共同进步,呵呵 [java] view plaincopy package com.example.myimageview; import

Android自定义imageview可对图片进行多点缩放和拖动

package com.msstudent.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; i

Android自定义带边框的圆形view

由于项目需要,需要做一个圆形的带边框并且里边还有文字的view →_→ ↓↓↓↓这样↓↓↓↓ 如果在布局文件中做的话是非常麻烦的,而且复用性也不高.所以想到用自定义一个view的来实现该功能,这样封装性和复用性就会相对提高,可方便在以后类似的项目中使用.可能也有同学有过这样的需求,所以在这分享出来供大家参考,不足之处还请多多指点. 看代码: 1package com.stock.manage.friend.view;import android.content.Context; 2 import

android自定义ImageView仿图片上传

Activity代码 1 public class MainActivity extends AppCompatActivity {   2     ProcessImageView processImageView =null;   3     int progress=0;   4    5     @Override   6     protected void onCreate(Bundle savedInstanceState) {   7         super.onCreate

android自定义view-打造圆形ImageView(四)终结篇

前言: 说实话,这段时间忙着修改毕业论文,好长时间没有碰代码了,真是罪过呀.今天我们就来奉上我们打造圆形ImageView的终结篇,以后如果还有新的创意再说啦.本文是在前面三篇的基础上得来的,详细请戳android自定义view-打造圆形ImageView(一).android自定义view-打造圆形ImageView(二).android自定义view-打造圆形ImageView(三). 效果图: 正文: 其实看了上面的效果图,大家应该都一目了然了,就是很多应用经常见到的带有白色边缘的渐变头像

Android自定义圆角ImageView

我们经常看到一些app中可以显示圆角图片,比如qq的联系人图标等等,实现圆角图片一种办法是直接使用圆角图片资源,当然如果没有圆角图片资源,我们也可以自己通过程序实现的,下面介绍一个自定义圆角ImageView的方法: package com.yulongfei.imageview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; impor