• 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

    创作的过程中有一部分代码參照了网上的样例,如有雷同。请见谅。
  • 相关阅读:
    BZOJ1045 [HAOI2008]糖果传递 && BZOJ3293 [Cqoi2011]分金币
    [BZOJ1103][POI2007]大都市meg dfs序+树状数组
    [BZOJ1122][POI2008]账本BBB 单调队列+后缀和
    [BZOJ1131][POI2008]Sta
    [BZOJ1370][Baltic2003]Gang团伙 并查集+拆点
    网易云基于 Kubernetes 的深度定制化实践
    微服务化的基石——持续集成
    微服务的接入层设计与动静资源隔离
    从互联网+角度看云计算的现状与未来(2)
    从互联网+角度看云计算的现状与未来(1)
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7232780.html
Copyright © 2020-2023  润新知