仿QQ消息导航栏RadioGroup里添加拖拽的TextView(未读消息)
2017-07-19 00:15
477 查看
效果图:
MainActivity:
主要是放置拖拽的TextView的位置setTextView(); 设置未读消息 textView.setText(“10”);
布局activity_main:
刚开始项目就已经使用RadioGroup了,本来准备在RadioButton里面动手脚的,后来发现不好测量,会与RadioButton的点击发生冲突,后来使用自定义的TipButton是可以实现未读消息是小圆点的,然后就是使用帧布局将RadioGroup和TextView包裹,然后进行测量TextView的宽就行了,效果还不错.
自定义的TipButton:
setTipOn(true)是可以设置小红点的
然后自定义的DragPointView:
源码demo
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" /> 11ea6 </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
相关文章推荐
- Android RadioGroup+ViewPager+ActionBar实现仿微信6.0界面(底部滑动菜单栏+导航栏)
- Android UI, TextView, Button, ToggleButton, RadioGroup
- Android之RadioGroup+ViewPager制作的底部导航栏
- android自定义view之模拟qq消息拖拽删除效果
- Android之RadioGroup+ViewPager制作的底部导航栏
- Android之RadioGroup+ViewPager制作的底部导航栏
- Android开发系列(十七)QQ聊天之Android显示Gif ——在TextView中添加动态表情
- 08-21 RadioGroup,RadioButton autoLink属性 TextView中ellipsize属性 TextView的属性
- Android之RadioGroup+ViewPager制作的底部导航栏
- ViewPager与RadioGroup制作导航栏
- 自定义view——贝塞尔曲线之仿qq消息气泡拖拽让所有view拖动爆炸
- 自定义view——贝塞尔曲线之仿qq消息气泡拖拽
- 实例—ViewPager+RadioGroup实现底部导航栏和页面的滑动
- Android练习之EditText、ImageButton、CheckBox、RadioGroup、ImageView、Spinner
- android 底部导航栏 ViewPager+RadioGroup+Fragment
- 解决TextView与RadioGroup不对齐的问题
- 自定义导航栏HorizontalScrollView+RadioGroup实现
- QQ聊天之Android显示Gif ——在TextView中添加动态表情
- 底部导航栏实现页面的切换(三):Fragment + RadioGroup + ViewPager
- RadioGroup+ViewPager制作的底部导航栏