zoukankan      html  css  js  c++  java
  • 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

    创作的过程中有一部分代码參照了网上的样例,如有雷同。请见谅。
  • 相关阅读:
    HAProxy、Keepalived 在 Ocatvia 的应用实现与分析
    Octavia 的 HTTPS 与自建、签发 CA 证书
    Octavia 创建 loadbalancer 的实现与分析
    OpenStack Rally 质量评估与自动化测试利器
    自建 CA 中心并签发 CA 证书
    Failed building wheel for netifaces
    通过 vSphere WS API 获取 vCenter Datastore Provisioned Space 置备空间
    OpenStack Placement Project
    我们建了一个 Golang 硬核技术交流群(内含视频福利)
    没有图形界面的软件有什么用?
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7232780.html
Copyright © 2011-2022 走看看