Android自定义View实现点赞控件

本文实例为大家分享了Android点赞控件的具体代码,供大家参考,具体内容如下

预览效果

目录

图片类:LikeImageView
文字类:LikeCharTextView
整合类:LikeView.java
自定义属性:attrs.xml

代码

LikeCharTextView

public class LikeCharTextView extends View {

 public static final int DEFAULT_TEXTCOLOR = Color.BLACK;
 public static final int DEFAULT_TEXTSIZE = 36;

 private TextPaint newTextPaint, oldTextPaint;

 private AnimatorSet addAnimator;
 private AnimatorSet minusAnimator;

 private int  measureWidth;
 private int  measureHeight;

 private int  textColor  = DEFAULT_TEXTCOLOR;
 private int  textSize  = DEFAULT_TEXTSIZE;

 private int  num;
 private int  oldNum;
 private int  newNum;

 private int  animatorOldY;
 private float  animatorOldAlpha = 1;

 private int  animatorNewY;
 private float  animatorNewAlpha = 0;
 private int  baseline;

 public LikeCharTextView(Context context) {
 super(context);

 init();
 }

 public LikeCharTextView(Context context, @Nullable AttributeSet attrs) {
 super(context, attrs);

 initAttr(context, attrs);

 init();
 }

 public LikeCharTextView(Context context, @Nullable AttributeSet attrs,
    int defStyleAttr) {
 super(context, attrs, defStyleAttr);

 initAttr(context, attrs);

 init();
 }

 /**
 * 初始化属性
 *
 * @param context
 * @param attrs
 */
 private void initAttr(Context context, @Nullable AttributeSet attrs) {
 TypedArray typedArray = context.obtainStyledAttributes(attrs,
  R.styleable.LikeCharTextView);
 textColor = typedArray.getColor(R.styleable.LikeCharTextView_textColor,
  DEFAULT_TEXTCOLOR);
 textSize = typedArray.getDimensionPixelSize(R.styleable.LikeCharTextView_textSize,
  DEFAULT_TEXTSIZE);
 num = typedArray.getInt(R.styleable.LikeCharTextView_number, 0);
 if (0 > num || num > 10) {
  throw new IllegalArgumentException("Number is only 0-9");
 }
 oldNum = num;

 typedArray.recycle();
 }

 /**
 * 初始化
 */
 private void init() {
 initPaints();
 initParams();
 }

 /**
 * 初始化画笔
 */
 private void initPaints() {
 newTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
 newTextPaint.setStyle(Paint.Style.FILL);
 newTextPaint.setTextSize(textSize);
 newTextPaint.setColor(textColor);
 newTextPaint.setTextAlign(Paint.Align.CENTER);

 oldTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
 oldTextPaint.set(newTextPaint);
 }

 /**
 * 初始化参数
 */
 private void initParams() {
 Paint.FontMetrics fontMetrics = newTextPaint.getFontMetrics();
 measureWidth = (int) newTextPaint.measureText(String.valueOf(num));
 measureHeight = (int) (fontMetrics.bottom - fontMetrics.top);

 float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
 baseline = (int) (measureHeight * 1.0f / 2 + distance);

 animatorOldY = baseline;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);

 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 switch (widthMode) {
  case MeasureSpec.UNSPECIFIED:
  break;
  case MeasureSpec.AT_MOST:
  widthSize = measureWidth;
  break;
  case MeasureSpec.EXACTLY:
  break;
 }

 int heightMode = MeasureSpec.getMode(widthMeasureSpec);
 int heightSize = MeasureSpec.getSize(widthMeasureSpec);
 switch (heightMode) {
  case MeasureSpec.UNSPECIFIED:
  break;
  case MeasureSpec.AT_MOST:
  heightSize = measureHeight;
  break;
  case MeasureSpec.EXACTLY:
  break;
 }

 setMeasuredDimension(widthSize, heightSize);
 }

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 int width = getWidth();
 int height = getHeight();
 oldTextPaint.setAlpha((int) (255 * animatorOldAlpha));
 canvas.drawText(String.valueOf(oldNum), width / 2, animatorOldY, oldTextPaint);

 newTextPaint.setAlpha((int) (255 * animatorNewAlpha));
 canvas.drawText(String.valueOf(newNum), width / 2, animatorNewY, newTextPaint);
 }

 public void setTextColor(int textColor) {
 this.textColor = textColor;
 init();
 invalidate();
 }

 public void setTextSize(int textSize) {
 this.textSize = textSize;
 init();
 invalidate();
 }

 public void setAnimatorOldY(int animatorOldY) {
 this.animatorOldY = animatorOldY;
 invalidate();
 }

 public void setAnimatorOldAlpha(float animatorOldAlpha) {
 this.animatorOldAlpha = animatorOldAlpha;
 invalidate();
 }

 public void setAnimatorNewY(int animatorNewY) {
 this.animatorNewY = animatorNewY;
 invalidate();
 }

 public void setAnimatorNewAlpha(float animatorNewAlpha) {
 this.animatorNewAlpha = animatorNewAlpha;
 invalidate();
 }

 public void setNum(int num) {
 this.num = num;
 if (0 > num || num > 10) {
  throw new IllegalArgumentException("Number is only 0-9");
 }
 oldNum = num;
 invalidate();
 }

 public void add() {
 Logger.e("执行加动画.基线:" + baseline);
 ObjectAnimator oldYAnimator = ObjectAnimator.ofInt(this, "animatorOldY", baseline, 0);
 ObjectAnimator oldAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorOldAlpha", 1, 0);
 ObjectAnimator newYAnimator = ObjectAnimator.ofInt(this, "animatorNewY", baseline * 2,
  baseline);
 ObjectAnimator newAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorNewAlpha", 0, 1);

 addAnimator = new AnimatorSet();
 addAnimator.playTogether(oldYAnimator, oldAlphaAnimator, newYAnimator, newAlphaAnimator);
 addAnimator.setInterpolator(new LinearInterpolator());
 addAnimator.setDuration(300);
 addAnimator.start();
 }

 public void minus() {
 Logger.e("执行减动画.基线:" + baseline);
 ObjectAnimator oldYAnimator = ObjectAnimator.ofInt(this, "animatorOldY", baseline,
  baseline * 2);
 ObjectAnimator oldAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorOldAlpha", 1, 0);
 ObjectAnimator newYAnimator = ObjectAnimator.ofInt(this, "animatorNewY", 0, baseline);
 ObjectAnimator newAlphaAnimator = ObjectAnimator.ofFloat(this, "animatorNewAlpha", 0, 1);

 minusAnimator = new AnimatorSet();
 minusAnimator.playTogether(oldYAnimator, oldAlphaAnimator, newYAnimator, newAlphaAnimator);
 minusAnimator.setInterpolator(new LinearInterpolator());
 minusAnimator.setDuration(300);
 minusAnimator.start();
 }

 public void change(boolean isAdd) {
 Logger.e("charTextVie.点击事件:" + isAdd);
 if (isAdd) {
  if (null != addAnimator && addAnimator.isStarted()) {
  Logger.e("charTextVie.加动画已执行.取消");
  addAnimator.cancel();
  }
  if (null != minusAnimator && minusAnimator.isStarted()) {
  Logger.e("charTextVie.减动画已执行.取消");
  minusAnimator.cancel();
  }
  sumNum(false);
  minus();
 } else {
  if (null != minusAnimator && minusAnimator.isStarted()) {
  Logger.e("charTextVie.减动画已执行.取消");
  minusAnimator.cancel();
  }
  if (null != addAnimator && addAnimator.isStarted()) {
  Logger.e("charTextVie.加动画已执行.取消");
  addAnimator.cancel();
  }
  sumNum(true);
  add();
 }
 }

 /**
 * 重新计算绘画的值
 *
 * @param isAdd
 */
 private void sumNum(boolean isAdd) {
 Logger.e("计算值开始");
 oldNum = num;
 newNum = num + (isAdd ? 1 : -1);
 if (newNum < 0) {
  newNum = 9;
 } else if (newNum > 9) {
  newNum = 0;
 }
 num = newNum;
 Logger.e("计算值结束:" + num);
 }
}

LikeImageView

public class LikeImageView extends View {

 private Paint imagePaint, shiningPaint;

 private int shiningMoveX;
 private int shiningMoveY;

 private int measureWidth;
 private int measureHeight;
 private Bitmap selectedBtimap;
 private Bitmap selectedShiningBtimap;
 private Bitmap unSelectedBtimap;

 private boolean isAdd = false;

 private float shiningAlpha = isAdd ? 1f : 0f;

 public LikeImageView(Context context) {
 super(context);

 init();
 }

 public LikeImageView(Context context, @Nullable AttributeSet attrs) {
 super(context, attrs);

 init();
 }

 public LikeImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);

 init();
 }

 private void init() {
 imagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 shiningPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

 selectedBtimap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_like_selected);
 selectedShiningBtimap = BitmapFactory.decodeResource(getResources(),
  R.mipmap.ic_like_selected_shining);
 unSelectedBtimap = BitmapFactory.decodeResource(getResources(),
  R.mipmap.ic_like_unselected);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);

 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 switch (widthMode) {
  case MeasureSpec.UNSPECIFIED:
  break;
  case MeasureSpec.AT_MOST:
  widthSize = Math.max(selectedBtimap.getWidth(), unSelectedBtimap.getWidth());
  shiningMoveX = (int) (widthSize * 1.0f - selectedShiningBtimap.getWidth()) - 2;
  break;
  case MeasureSpec.EXACTLY:
  break;
 }

 int heightMode = MeasureSpec.getMode(widthMeasureSpec);
 int heightSize = MeasureSpec.getSize(widthMeasureSpec);
 switch (heightMode) {
  case MeasureSpec.UNSPECIFIED:
  break;
  case MeasureSpec.AT_MOST:
  heightSize = Math.max(selectedBtimap.getHeight(), unSelectedBtimap.getHeight());
  shiningMoveY = (int) (selectedShiningBtimap.getHeight() * 1.0f / 3);
  heightSize += shiningMoveY;
  break;
  case MeasureSpec.EXACTLY:
  break;
 }

 setMeasuredDimension(widthSize, heightSize);
 }

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

 int width = getWidth();
 int height = getHeight();
 Rect src = new Rect(0, 0, width, height);
 Rect selectDst = new Rect(0, shiningMoveY, selectedBtimap.getWidth(), height);
 if (isAdd) {
  //画红赞
  canvas.drawBitmap(selectedBtimap, src, selectDst, imagePaint);
  //画阴影
  shiningPaint.setAlpha((int) (255 * shiningAlpha));
  Rect shiningDst = new Rect(shiningMoveX, 0,
   shiningMoveX + selectedShiningBtimap.getWidth(), selectedShiningBtimap.getHeight());
  canvas.drawBitmap(selectedShiningBtimap, src, shiningDst, shiningPaint);
 } else {
  //画灰赞
  canvas.drawBitmap(unSelectedBtimap, src, selectDst, imagePaint);
 }
 }

 public void setShiningAlpha(float shiningAlpha) {
 this.shiningAlpha = shiningAlpha;
 invalidate();
 }

 public void setAdd(boolean add) {
 isAdd = add;
 shiningAlpha = 1.0f;
 invalidate();
 }

 public void changeLike(boolean isAdd) {
 this.isAdd = !isAdd;
 invalidate();
 anim();
 }

 private void anim() {
 ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(this, "scaleX", 0.7f, 1f);
 scaleXAnim.setDuration(500);
 ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(this, "scaleY", 0.7f, 1f);
 scaleYAnim.setDuration(500);
 ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, "shiningAlpha", 0f, 1f);
 alphaAnim.setDuration(500);
 AnimatorSet animatorSet = new AnimatorSet();
 animatorSet.playTogether(scaleXAnim, scaleYAnim, alphaAnim);
 animatorSet.setInterpolator(new BounceInterpolator());
 animatorSet.start();
 }
}

LikeView

public class LikeView extends LinearLayout {
 private final int IMAGEPADDING = 4;

 private boolean isAdd = false;

 private int num;
 private int textSize;
 private int textColor;

 private int imagePadding;

 private List<LikeCharTextView> charTvs = new ArrayList<>();
 private LikeImageView likeImageView;

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

 public LikeView(Context context, @Nullable AttributeSet attrs) {
 super(context, attrs);

 initAttr(context, attrs);

 init();
 }

 public LikeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);

 initAttr(context, attrs);

 init();
 }

 private void initAttr(Context context, AttributeSet attrs) {
 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LikeView);
 textColor = typedArray.getColor(R.styleable.LikeView_textColor,
  LikeCharTextView.DEFAULT_TEXTCOLOR);
 textSize = typedArray.getDimensionPixelSize(R.styleable.LikeView_textSize,
  LikeCharTextView.DEFAULT_TEXTSIZE);
 num = typedArray.getInt(R.styleable.LikeView_number, 0);
 imagePadding = typedArray.getDimensionPixelSize(R.styleable.LikeView_imagePadding, IMAGEPADDING);

 typedArray.recycle();
 }

 /**
 * 初始化
 */
 private void init() {

 initView();
 }

 protected void initView() {
 removeAllViews();

 likeImageView = new LikeImageView(getContext());
 likeImageView.setAdd(isAdd);
 LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 layoutParams.rightMargin = imagePadding;
 likeImageView.setLayoutParams(layoutParams);
 addView(likeImageView);

 charTvs.clear();
 String str_num = String.valueOf(num);
 for (int i = 0; i < str_num.length(); i++) {
  LikeCharTextView textView = new LikeCharTextView(getContext());
  int show_num = Integer.valueOf(str_num.substring(i, i + 1));
  Log.e("zanview", "show_num:" + show_num);
  textView.setTextSize(textSize);
  textView.setTextColor(textColor);
  textView.setNum(show_num);
  addView(textView);
  charTvs.add(textView);
 }
 }

 public void setNum(int num) {
 this.num = num;
 init();
 invalidate();
 }

 public void setAdd(boolean add) {
 isAdd = add;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 //计算出所有的childView的宽高
 measureChildren(widthMeasureSpec, heightMeasureSpec);
 setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
 }

 /**
 * 测量宽度
 *
 * @param widthMeasureSpec
 * @return
 */
 private int measureWidth(int widthMeasureSpec) {
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 switch (widthMode) {
  case MeasureSpec.UNSPECIFIED:
  break;
  case MeasureSpec.AT_MOST:
  widthSize = 0;
  for (int i = 0; i < getChildCount(); i++) {
   View childView = getChildAt(i);
   //获取子view的宽
   int cWidth = childView.getMeasuredWidth();
   MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
   widthSize += cWidth + params.leftMargin + params.rightMargin;
  }
  break;
  case MeasureSpec.EXACTLY:
  break;
 }
 return widthSize;
 }

 /**
 * 测量高度
 *
 * @param widthMeasureSpec
 * @return
 */
 private int measureHeight(int widthMeasureSpec) {
 int heightMode = MeasureSpec.getMode(widthMeasureSpec);
 int heightSize = MeasureSpec.getSize(widthMeasureSpec);
 switch (heightMode) {
  case MeasureSpec.UNSPECIFIED:
  break;
  case MeasureSpec.AT_MOST:
  heightSize = 0;
  for (int i = 0; i < getChildCount(); i++) {
   View childView = getChildAt(i);
   //获取子view的宽
   int cWidth = childView.getMeasuredHeight();
   MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
   int height = cWidth + params.leftMargin + params.rightMargin;
   heightSize = Math.max(heightSize, height);
  }
  break;
  case MeasureSpec.EXACTLY:
  break;
 }
 return heightSize;
 }

 private boolean click = false;

 private final int MOHUFANWEI = 10;

 private float lastX = 0;
 private float lastY = 0;

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 super.onTouchEvent(event);
 float x = event.getX();
 float y = event.getY();

 switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:
  if (1 == event.getPointerCount()) {
   click = true;
  }
  break;
  case MotionEvent.ACTION_UP:
  if (click) {
   onClick();
  }
  break;
  case MotionEvent.ACTION_MOVE:
  if (Math.abs(lastX - x) > MOHUFANWEI || Math.abs(lastY - y) > MOHUFANWEI) {
   click = false;
  }
  break;
 }

 lastX = x;
 lastY = y;
 return true;
 }

 private void onClick() {
 Logger.e("点击事件" + isAdd);
 String str_num = String.valueOf(num);
 Logger.e("点击事件,str_num:" + str_num);
 boolean nextAnim = false;
 if (isAdd) {
  likeImageView.changeLike(true);
  for (int i = (str_num.length() - 1); i >= 0; i--) {
  int chr_num = Integer.valueOf(str_num.substring(i, i + 1));
  Logger.e("点击事件,chr_num:%d,charTvs.size:%d,i:%d", chr_num, charTvs.size(), i);
  Logger.e("是否执行动画:" + (charTvs.size() > i));
  if (charTvs.size() > i) {
   if (i == (str_num.length() - 1) || nextAnim) {
   Logger.e("点击事件,执行个位动画||%b执行执行上%d位动画", nextAnim, i);
   charTvs.get(i).change(true);

   chr_num--;
   Logger.e("chr_num:%d,是否执行上一位动画:", chr_num, (chr_num < 0));
   if (chr_num < 0) {
    nextAnim = true;
   } else {
    nextAnim = false;
   }

   Logger.e("nextAnim:" + nextAnim);
   }
  }
  }
  num--;
  isAdd = !isAdd;
 } else {
  likeImageView.changeLike(false);
  for (int i = (str_num.length() - 1); i >= 0; i--) {
  int chr_num = Integer.valueOf(str_num.substring(i, i + 1));
  Logger.e("点击事件,chr_num:%d,charTvs.size:%d,i:%d", chr_num, charTvs.size(), i);
  Logger.e("是否执行动画:" + (charTvs.size() > i));
  if (charTvs.size() > i) {
   if (i == (str_num.length() - 1) || nextAnim) {
   Logger.e("点击事件,执行个位动画||%b执行执行上%d位动画", nextAnim, i);
   charTvs.get(i).change(false);

   chr_num++;
   Logger.e("chr_num:%d,是否执行上一位动画:", chr_num, (chr_num > 9));
   if (chr_num > 9) {
    nextAnim = true;
   } else {
    nextAnim = false;
   }
   Logger.e("nextAnim:" + nextAnim);
   }
  }
  }
  num++;
  isAdd = !isAdd;
 }
 }
}

attrs.xml

<attr name="textSize" format="dimension" />
 <attr name="textColor" format="color" />
 <attr name="number" format="integer" />

 <attr name="imageWidth" format="dimension" />
 <attr name="imageHeight" format="dimension" />

 <declare-styleable name="LikeView">
 <attr name="textSize" />
 <attr name="textColor" />
 <attr name="number" />
 <attr name="imageWithd" />
 <attr name="imageHeight" />
 <attr name="imagePadding" format="dimension" />
 </declare-styleable>

 <declare-styleable name="LikeCharTextView">
 <attr name="textSize" />
 <attr name="textColor" />
 <attr name="number" />
 </declare-styleable>

 <declare-styleable name="LikeImageView">
 <attr name="imageWithd" />
 <attr name="imageHeight" />
</declare-styleable>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android仿微信朋友圈点赞和评论功能

    最近在做朋友圈的项目,所以写一个Android仿朋友圈点赞和评论功能Demo,代码就是简单实现了一下功能,没有做优化,凑合看 图文排列是用的RecyclerView实现的,弹窗效果是用的自定义的PopupWindow,点赞应该是在本地请求数据库,设置一个flag,获取当前用户的id后,带着id向服务器post一个flag,评论就比较简单了,也是获取当前朋友id(或者昵称),带着内容,向服务器post 贴代码: package com.example.lenovo.dianzandemo; imp

  • Android自定义view实现仿抖音点赞效果

    前言 学习自定义view,想找点东西耍一下,刚好看到抖音的点赞效果不错,尝试一下. 抖音效果: 话不多说,先上代码: public class Love extends RelativeLayout { private Context mContext; float[] num = {-30, -20, 0, 20, 30};//随机心形图片角度 public Love(Context context) { super(context); initView(context); } public

  • Android项目开发 教你实现Periscope点赞效果

    现在视频应用越来越火,Periscope火起来后,国内也出现了不少跟风者,界面几乎跟Periscope一模一样.Periscope确实不错,点赞的效果也让人眼前一亮,很漂亮,于是乎,我就想着自己实现一下. 最终的效果图如下: 最终效果图.gif 录制的效果不太好,手机运行起来还是挺好看的. 不能说一模一样,但是也差不多了吧!~ 惯例: 通过本文你将学习到什么? 自定义view的一些基础方法以及一些注意点 随机数的使用 插补器的使用 属性动画的高级用法 贝塞尔曲线在Android中的实现以及应用

  • Android控件实现直播App特效之点赞飘心动画

    现在市面上直播类的应用可以说是一抓一大把,随随便便就以什么主题来开发个直播App,说白了就想在这领域分杯羹.在使用这些应用过程中其实不难发现,在所有的直播界面,少不了的就是各种打赏.各种点赞.今天自己就针对点赞功能敲了一下,代码不多,主要是涉及到动画运动轨迹运算,这里需借助 贝塞尔曲线 相关知识,我使用三阶贝塞尔曲线来实现轨迹动画. 运行效果 一.具体实现流程 仔细分析整个点赞过程可以发现,首先是"爱心"的出现动画,然后是"爱心"以类似气泡的形式向上运动. &quo

  • Android中Listview点赞功能的实现

    最近这段时间一直在看Android,利用Listview去实现点赞功能,下面给大家介绍下基本思路. 基本思路: 进入界面–>获取数据–> 在Listview中显示–> 通过map集合(position,boolean)保存每一行是否被点击–> 利用实体类去保存相应的对象–> get/set方法进行相应值得改变–> 点击一次,相应的数量加1 只实现了点赞功能,踩和赞基本类似. 具体实现如下: 继承自BaseAdapter package com.gz.test_listv

  • 简单实用的Android UI微博动态点赞效果

    说起空间动态.微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆.而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧.毕竟,也就是两个view和一些简单的动画效果罢了. 单若是只讲这些,我自然也是不愿花这番功夫的.虽然自己很菜,可也不甘于太菜.所以偶尔看到些好东西,可以延伸学写下,我还是很情愿拿出来用用,顺带秀一秀逼格什么的. 不扯太多,先说说今天实现点赞效果用到的自以为不错的两个点: Checkable 用来扩展View实现选中状态切

  • Android 仿微信朋友圈点赞和评论弹出框功能

    贡献/下载源码:https://github.com/mmlovesyy/PopupWindowDemo 本文简单模仿微信朋友圈的点赞和评论弹出框,布局等细节请忽略,着重实现弹出框.发评论,及弹出位置的控制. 1. 微信弹出框 微信朋友圈的点赞和评论功能,有2个组成部分: 点击左下角的"更多"按钮,弹出对话框: 点击评论,弹出输入框,添加评论并在页面中实时显示: 微信朋友圈点赞和评论功能 2. 实际效果 本文将建一个 ListView,在其 Item 中简单模仿微信的布局,然后着重实现

  • Android高级UI特效仿直播点赞动画效果

    本文给大家分享高级UI特效仿直播点赞效果-一个优美炫酷的点赞动画,具体实现代码大家参考本文. 效果图如下: 攻克难点: 心形图片的路径等走向 心形图片的控制范围 部分代码如下: 通过AbstractPathAnimator定义飘心动画控制器 @Override public void start(final View child, final ViewGroup parent) { parent.addView(child, new ViewGroup.LayoutParams(mConfig.

  • Android自定义ViewGroup实现堆叠头像的点赞Layout

    简介 这样的点赞列表怎么样?之前做社区的时候也有类似的点赞列表,但是没有这样重叠,一个小小的改变,个人感觉逼格提高不少. 这个很有规则,就是后一个头像会覆盖一部分到前一个头像上,头像多了就像一串糖葫芦了. 这个实现起来不难,自定义ViewGroup,关键重写onLayout方法. 关于自定义控件的基础知识可以看一看这个,整理的很详细: https://github.com/GcsSloop/AndroidNote 实现 自定义属性 属性名 说明 默认值 vertivalSpace 行距 4dp

  • Android中使用PopupWindow 仿微信点赞和评论弹出

    微信朋友圈的点赞和评论功能,有2个组成部分:左下角的"更多"按钮:点击该按钮后弹出的对话框: PopupWindow,弹出框使用PopupWindow实现,这是点赞和评论的载体,具体要涉及 PopupWindow 点击非窗口位置和再次点击消失以及显示位置的问题(根据相应更多按钮的位置确定 PopupWindow 的显示位置 package com.example.cmm.helloworld; import android.app.AlertDialog; import android

随机推荐