1. 程式人生 > >仿QQ訊息導航欄RadioGroup裡新增拖拽的TextView(未讀訊息)

仿QQ訊息導航欄RadioGroup裡新增拖拽的TextView(未讀訊息)

效果圖:

未讀訊息

MainActivity:
主要是放置拖拽的TextView的位置setTextView(); 設定未讀訊息 textView.setText(“10”);

public class MainActivity extends AppCompatActivity {

    private DragPointView textView;
    private RadioGroup group;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super
.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化 textView =(DragPointView)findViewById(R.id.dpv_message); group = (RadioGroup)findViewById(R.id.group); //放置拖拽的TexView setTextView(); group.setOnCheckedChangeListener(listener); } private
RadioGroup.OnCheckedChangeListener listener = new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup radioGroup, int checkedId) { if (checkedId == R.id.tab_rb_news) { } else if (checkedId == R.id.tab_rb_contacts) { } else
if (checkedId == R.id.tab_rb_discover) { } else if (checkedId == R.id.tab_rb_service) { } else if (checkedId == R.id.tab_rb_myself) { } } }; private void setTextView() { DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int screenWidth = displayMetrics.widthPixels; FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) textView.getLayoutParams(); layoutParams.leftMargin = screenWidth * 8/ 25; textView.setLayoutParams(layoutParams); textView.setText("10"); textView.setBackgroundColor(Color.RED); } }

佈局activity_main:
剛開始專案就已經使用RadioGroup了,本來準備在RadioButton裡面動手腳的,後來發現不好測量,會與RadioButton的點擊發生衝突,後來使用自定義的TipButton是可以實現未讀訊息是小圓點的,然後就是使用幀佈局將RadioGroup和TextView包裹,然後進行測量TextView的寬就行了,效果還不錯.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.myapplication.MainActivity">


        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            />


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFFFF"
        android:minHeight="48dp"
        >

        <RadioGroup
            android:id="@+id/group"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#FFFFFF"
            android:gravity="center"
            android:minHeight="48dp"
            android:orientation="horizontal"
            >

            <com.example.myapplication.TipButton
                android:id="@+id/tab_rb_news"
                style="@style/radioButton"
                android:button="@null"
                android:checked="true"
                android:drawableTop="@drawable/tab_schoolmate_news_selector"
                android:text="校友會" />

            <com.example.myapplication.TipButton
                android:id="@+id/tab_rb_contacts"
                style="@style/radioButton"
                android:button="@null"
                android:drawableTop="@drawable/tab_schoolmate_contacts_selector"
                android:text="訊息" />

            <com.example.myapplication.TipButton
                android:id="@+id/tab_rb_discover"
                android:button="@null"
                style="@style/radioButton"
                android:drawableTop="@drawable/tab_discover_selector"
                android:text="發現" />


            <com.example.myapplication.TipButton
                android:id="@+id/tab_rb_service"
                android:button="@null"
                style="@style/radioButton"
                android:drawableTop="@drawable/tab_schoolmate_service_selector"
                android:text="服務" />

            <com.example.myapplication.TipButton
                android:id="@+id/tab_rb_myself"
                style="@style/radioButton"
                android:button="@null"
                android:drawableTop="@drawable/tab_schoolmate_self_selector"
                android:text="我的" />

        </RadioGroup>

        <com.example.myapplication.DragPointView
            android:id="@+id/dpv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="top|left|center"
            android:layout_marginTop="3dp"
            android:layout_marginLeft="120dp"
            android:background="@drawable/shapecount"
            android:backgroundTint="@color/red"
            android:gravity="center"
            android:paddingLeft="3dp"
            android:paddingRight="3dp"
            android:text="10"
            android:textColor="#ffffffff"
            android:textSize="11sp" />

    </FrameLayout>
</LinearLayout>

自定義的TipButton:
setTipOn(true)是可以設定小紅點的

/**
 * Create by xuke  2017/7/17
 * desc:可以顯示小圓點的RadioButton
 * */

@SuppressLint("AppCompatCustomView")
public class TipButton extends RadioButton {

    private boolean mTipOn = false;

    private Dot mDot;

    private class Dot {

        int color;
        int radius;
        int marginTop;
        int marginRight;

        Dot() {
            float density = getContext().getResources().getDisplayMetrics().density;
            radius = (int) (5 * density);
            marginTop = (int) (3 * density);
            marginRight = (int) (3 * density);

            color = getContext().getResources().getColor(R.color.red);
        }

    }

    public TipButton(Context context) {
        super(context);
        init();
    }

    public TipButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TipButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mDot = new Dot();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mTipOn) {
            float cx = getWidth() - mDot.marginRight - mDot.radius;
            float cy = mDot.marginTop + mDot.radius;

            Drawable drawableTop = getCompoundDrawables()[1];
            if (drawableTop != null) {
                int drawableTopWidth = drawableTop.getIntrinsicWidth();
                if (drawableTopWidth > 0) {
                    int dotLeft = getWidth() / 2 + drawableTopWidth / 2;
                    cx = dotLeft + mDot.radius;
                }
            }


            Paint paint = getPaint();
            //save
            int tempColor = paint.getColor();

            paint.setColor(mDot.color);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(cx, cy, mDot.radius, paint);

            //restore
            paint.setColor(tempColor);
        }
    }

    public void setTipOn(boolean tip) {
        this.mTipOn = tip;

        invalidate();
    }

    public boolean isTipOn() {
        return mTipOn;
    }
}

然後自定義的DragPointView:

/**
 * Create by xuke  2017/7/17
 * desc:可以拖拽的TextView
 * */

@SuppressLint("AppCompatCustomView")
public class DragPointView extends TextView {
    private boolean initBgFlag;
    private OnDragListencer dragListencer;
    private int backgroundColor = Color.parseColor("#0F88EB");
    private PointView pointView;
    private int x, y, r;
    private ViewGroup scrollParent;
    private int[] p = new int[2];

    public DragPointView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initbg();
    }

    public OnDragListencer getDragListencer() {
        return dragListencer;
    }

    public void setDragListencer(OnDragListencer dragListencer) {
        this.dragListencer = dragListencer;
    }

    public int getBackgroundColor() {
        return backgroundColor;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int w = getMeasuredWidth();
        int h = getMeasuredHeight();
        if (w != h) { // 簡單的將寬高搞成一樣的,如果有更好的方法歡迎在我部落格下方留言!
            int x = Math.max(w, h);
            setMeasuredDimension(x, x);
        }
    }

    @SuppressWarnings("deprecation")
    public void setBackgroundColor(int backgroundColor) {
        this.backgroundColor = backgroundColor;
        DragPointView.this.setBackgroundDrawable(createStateListDrawable((getHeight() > getWidth() ? getHeight()
                : getWidth()) / 2, backgroundColor));
    }

    private void initbg() {
        setGravity(Gravity.CENTER);
        getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {

            @SuppressWarnings("deprecation")
            @Override
            public boolean onPreDraw() {
                if (!initBgFlag) {
                    DragPointView.this.setBackgroundDrawable(createStateListDrawable(
                            (getHeight() > getWidth() ? getHeight() : getWidth()) / 2, backgroundColor));
                    initBgFlag = true;
                    return false;
                }
                return true;
            }
        });
    }


    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        View root = getRootView();
        if (root == null || !(root instanceof ViewGroup)) {
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                root.getLocationOnScreen(p);
                scrollParent = getScrollParent();
                if (scrollParent != null) {
                    scrollParent.requestDisallowInterceptTouchEvent(true);
                }
                int location[] = new int[2];
                getLocationOnScreen(location);
                x = location[0] + (getWidth() / 2) - p[0];
                y = location[1] + (getHeight() / 2) - p[1];
                r = (getWidth() + getHeight()) / 4;
                pointView = new PointView(getContext());
                pointView.setLayoutParams(new ViewGroup.LayoutParams(root.getWidth(), root.getHeight()));
                setDrawingCacheEnabled(true);
                pointView.catchBitmap = getDrawingCache();
                pointView.setLocation(x, y, r, event.getRawX() - p[0], event.getRawY() - p[1]);
                ((ViewGroup) root).addView(pointView);
                setVisibility(View.INVISIBLE);
                break;
            case MotionEvent.ACTION_MOVE:
                pointView.refrashXY(event.getRawX() - p[0], event.getRawY() - p[1]);
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (scrollParent != null) {
                    scrollParent.requestDisallowInterceptTouchEvent(false);
                }
                if (!pointView.broken) { // 沒有拉斷
                    pointView.cancel();
                } else if (pointView.nearby) {// 拉斷了,但是又回去了
                    pointView.cancel();
                } else { // 徹底拉斷了
                    pointView.broken();
                }
                break;
            default:
                break;
        }
        return true;
    }

    private ViewGroup getScrollParent() {
        View p = this;
        while (true) {
            View v;
            try {
                v = (View) p.getParent();
            } catch (ClassCastException e) {
                return null;
            }
            if (v == null)
                return null;
            if (v instanceof AbsListView || v instanceof ScrollView || v instanceof ViewPager) {
                return (ViewGroup) v;
            }
            p = v;
        }
    }

    public interface OnDragListencer {
        public void onDragOut();
    }

    class PointView extends View {
        private Bitmap catchBitmap;
        private Circle c1;
        private Circle c2;
        private Paint paint;
        private Path path = new Path();
        private int maxDistance = 10; // 10倍半徑距離視為拉斷
        private boolean broken; // 是否拉斷過
        private boolean out; // 放手的時候是否拉斷
        private boolean nearby;
        private int brokenProgress;

        public PointView(Context context) {
            super(context);
            init();
        }

        public void init() {
            paint = new Paint();
            paint.setColor(backgroundColor);
            paint.setAntiAlias(true);
        }

        public void setLocation(float c1X, float c1Y, float r, float endX, float endY) {
            broken = false;
            c1 = new Circle(c1X, c1Y, r);
            c2 = new Circle(endX, endY, r);
        }

        public void refrashXY(float x, float y) {
            c2.x = x;
            c2.y = y;
            // 以前的半徑應該根據距離縮小點了
            // 計算出距離
            double distance = c1.getDistance(c2);
            int rate = 10;
            c1.r = (float) ((c2.r * c2.r * rate) / (distance + (c2.r * rate)));
            invalidate();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.TRANSPARENT);
            if (out) {
                float dr = c2.r / 2 + c2.r * 4 * (brokenProgress / 100f);
                canvas.drawCircle(c2.x, c2.y, c2.r / (brokenProgress + 1), paint);
                canvas.drawCircle(c2.x - dr, c2.y - dr, c2.r / (brokenProgress + 2), paint);
                canvas.drawCircle(c2.x + dr, c2.y - dr, c2.r / (brokenProgress + 2), paint);
                canvas.drawCircle(c2.x - dr, c2.y + dr, c2.r / (brokenProgress + 2), paint);
                canvas.drawCircle(c2.x + dr, c2.y + dr, c2.r / (brokenProgress + 2), paint);
            } else {
                // 繪製手指跟蹤的圓形
                canvas.drawBitmap(catchBitmap, c2.x - c2.r, c2.y - c2.r, paint);
                path.reset();
                float deltaX = c2.x - c1.x;
                float deltaY = -(c2.y - c1.y);
                double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
                double sin = deltaY / distance;
                double cos = deltaX / distance;
                nearby = distance < c2.r * maxDistance;
                if (nearby && !broken) {
                    canvas.drawCircle(c1.x, c1.y, c1.r, paint);
                    path.moveTo((float) (c1.x - c1.r * sin), (float) (c1.y - c1.r * cos));
                    path.lineTo((float) (c1.x + c1.r * sin), (float) (c1.y + c1.r * cos));
                    path.quadTo((c1.x + c2.x) / 2, (c1.y + c2.y) / 2, (float) (c2.x + c2.r * sin), (float) (c2.y + c2.r
                            * cos));
                    path.lineTo((float) (c2.x - c2.r * sin), (float) (c2.y - c2.r * cos));
                    path.quadTo((c1.x + c2.x) / 2, (c1.y + c2.y) / 2, (float) (c1.x - c1.r * sin), (float) (c1.y - c1.r
                            * cos));
                    canvas.drawPath(path, paint);
                } else {
                    broken = true; // 已經拉斷了
                }
            }

        }

        public void cancel() {
            int duration = 150;
            AnimatorSet set = new AnimatorSet();
            ValueAnimator animx = ValueAnimator.ofFloat(c2.x, c1.x);
            animx.setDuration(duration);
            animx.setInterpolator(new OvershootInterpolator(2));
            animx.addUpdateListener(new AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    c2.x = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            ValueAnimator animy = ValueAnimator.ofFloat(c2.y, c1.y);
            animy.setDuration(duration);
            animy.setInterpolator(new OvershootInterpolator(2));
            animy.addUpdateListener(new AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    c2.y = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            set.playTogether(animx, animy);
            set.addListener(new AnimatorListenerAdapter() {

                @Override
                public void onAnimationEnd(Animator animation) {
                    ViewGroup vg = (ViewGroup) PointView.this.getParent();
                    vg.removeView(PointView.this);
                    DragPointView.this.setVisibility(View.VISIBLE);
                }
            });
            set.start();

        }

        public void broken() {
            out = true;
            int duration = 500;
            ValueAnimator a = ValueAnimator.ofInt(0, 100);
            a.setDuration(duration);
            a.setInterpolator(new LinearInterpolator());
            a.addUpdateListener(new AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    brokenProgress = (int) animation.getAnimatedValue();
                    invalidate();
                }
            });
            a.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    ViewGroup vg = (ViewGroup) PointView.this.getParent();
                    vg.removeView(PointView.this);
                }
            });
            a.start();
            if (dragListencer != null) {
                dragListencer.onDragOut();
            }
        }

        class Circle {
            float x;
            float y;
            float r;

            public Circle(float x, float y, float r) {
                this.x = x;
                this.y = y;
                this.r = r;
            }

            public double getDistance(Circle c) {
                float deltaX = x - c.x;
                float deltaY = y - c.y;
                double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
                return distance;
            }
        }

    }

    /**
     * @param radius 圓角角度
     * @param color  填充顏色
     * @return StateListDrawable 物件
     * @author zy
     */
    public static StateListDrawable createStateListDrawable(int radius, int color) {
        StateListDrawable bg = new StateListDrawable();
        GradientDrawable gradientStateNormal = new GradientDrawable();
        gradientStateNormal.setColor(color);
        gradientStateNormal.setShape(GradientDrawable.RECTANGLE);
        gradientStateNormal.setCornerRadius(radius);
        gradientStateNormal.setStroke(0, 0);
        bg.addState(View.EMPTY_STATE_SET, gradientStateNormal);
        return bg;
    }

    // 分別記錄上次滑動的座標(onInterceptTouchEvent)
    private int mLastXIntercept = 0;
    private int mLastYIntercept = 0;

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int x = (int) ev.getX();
        int y = (int) ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastXIntercept;
                int deltaY = y - mLastYIntercept;
                //如果是左右滑動
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }
        }
        mLastXIntercept = x;
        mLastYIntercept = y;
        return super.dispatchTouchEvent(ev);
    }
}

原始碼demo