Android仿知乎悬浮功能按钮FloatingActionButton效果

前段时间在看属性动画,恰巧这个按钮的效果可以用属性动画实现,所以就来实践实践。效果基本出来了,大家可以自己去完善。

首先看一下效果图:

我们看到点击FloatingActionButton后会展开一些item,然后会有一个蒙板效果,这都是这个View的功能。那么这整个View肯定是个ViewGroup,我们一部分一部分来看。

首先是这个最小的Tag:

这个Tag带文字,可以是一个TextView,但为了美观,我们使用CardView,CardView是一个FrameLayout,我们要让它具有显示文字的功能,就继承CardView自定义一个ViewGroup。

public class TagView extends CardView

内部维护一个TextView,在其构造函数中我们实例化一个TextView用来显示文字,并在外部调用setTagText的时候把TextView添加到这个CardView中。

public class TagView extends CardView {
 private TextView mTextView;
 public TagView(Context context) {
 this(context, null);
 }
 public TagView(Context context, AttributeSet attrs) {
 this(context, attrs, 0);
 }
 public TagView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 mTextView = new TextView(context);
 mTextView.setSingleLine(true);
 }
 protected void setTextSize(float size){
 mTextView.setTextSize(size);
 }
 protected void setTextColor(int color){
 mTextView.setTextColor(color);
 }
 //给内部的TextView添加文字
 protected void setTagText(String text){
 mTextView.setText(text);
 addTag();
 }
 //添加进这个layout中
 private void addTag(){
 LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
  , ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
 int l = dp2px(8);
 int t = dp2px(8);
 int r = dp2px(8);
 int b = dp2px(8);
 layoutParams.setMargins(l, t, r, b);
 //addView会引起所有View的layout
 addView(mTextView, layoutParams);
 }
 private int dp2px(int value){
 return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
  , value, getResources().getDisplayMetrics());
 }
}

接下来我们看这个item,它是一个tag和一个fab的组合:

tag使用刚才我们自定义的TagView,fab就用系统的FloatingActionButton,这里显然需要一个ViewGroup来组合这两个子View,可以使用LinearLayout,这里我们就直接使用ViewGroup。

public class TagFabLayout extends ViewGroup

我们为这个ViewGroup设置自定义属性,是为了给tag设置text:

 <declare-styleable name="FabTagLayout">
 <attr name="tagText" format="string" />
 </declare-styleable>

在构造器中获取自定义属性,初始化TagView并添加到该ViewGroup中:

 public TagFabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 getAttributes(context, attrs);
 settingTagView(context);
 }
 private void getAttributes(Context context, AttributeSet attributeSet){
 TypedArray typedArray = context.obtainStyledAttributes(attributeSet
  , R.styleable.FabTagLayout);
 mTagText = typedArray.getString(R.styleable.FabTagLayout_tagText);
 typedArray.recycle();
 }
 private void settingTagView(Context context){
 mTagView = new TagView(context);
 mTagView.setTagText(mTagText);
 addView(mTagView);
 }

在onMeasure对该ViewGroup进行测量,这里我直接把宽高设置成wrap_content的了,match_parent和精确值感觉没有必要。TagView和FloatingActionButton横向排列,中间和两边留一点空隙。

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int width = 0;
 int height = 0;
 int count = getChildCount();
 for(int i=0; i<count; i++){
  View view = getChildAt(i);
  measureChild(view, widthMeasureSpec, heightMeasureSpec);
  width += view.getMeasuredWidth();
  height = Math.max(height, view.getMeasuredHeight());
 }
 width += dp2px(8 + 8 + 8);
 height += dp2px(8 + 8);
 //直接将该ViewGroup设定为wrap_content的
 setMeasuredDimension(width, height);
 }

在onLayout中横向布局,tag在左,fab在右。

@Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 //为子View布局
 View tagView = getChildAt(0);
 View fabView = getChildAt(1);
 int tagWidth = tagView.getMeasuredWidth();
 int tagHeight = tagView.getMeasuredHeight();
 int fabWidth = fabView.getMeasuredWidth();
 int fabHeight = fabView.getMeasuredHeight();
 int tl = dp2px(8);
 int tt = (getMeasuredHeight() - tagHeight) / 2;
 int tr = tl + tagWidth;
 int tb = tt + tagHeight;
 int fl = tr + dp2px(8);
 int ft = (getMeasuredHeight() - fabHeight) / 2;
 int fr = fl + fabWidth;
 int fb = ft + fabHeight;
 fabView.layout(fl, ft, fr, fb);
 tagView.layout(tl, tt, tr, tb);
 bindEvents(tagView, fabView);
 }

还要为这两个子View注册OnClickListener,这是点击事件传递的源头。

private void bindEvents(View tagView, View fabView){
 tagView.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
  if(mOnTagClickListener != null){
   mOnTagClickListener.onTagClick();
  }
  }
 });
 fabView.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
  if (mOnFabClickListener != null){
   mOnFabClickListener.onFabClick();
  }
  }
 });
 }

现在item的ViewGroup有了,我们还需要一个蒙板,一个主fab,那么我们来看最终的ViewGroup。

思路也很清楚,蒙板是match_parent的,主fab在右下角(当然我们可以自己设置,也可以对外提供接口来设置位置),三个item(也就是TagFabLayout)在主fab的上面。至于动画效果,在点击事件中触发。

public class MultiFloatingActionButton extends ViewGroup

这里我们还需要自定义一些属性,比如蒙板的颜色、主Fab的颜色、主Fab的图案(当然,你把主Fab直接写在xml中就可以直接定义这些属性)、动画的duaration、动画的模式等。

 <attr name="animationMode">
 <enum name="fade" value="0"/>
 <enum name="scale" value="1"/>
 <enum name="bounce" value="2"/>
 </attr>
 <attr name="position">
 <enum name="left_bottom" value="0"/>
 <enum name="right_bottom" value="1"/>
 </attr>
 <declare-styleable name="MultiFloatingActionButton">
 <attr name="backgroundColor" format="color"/>
 <attr name="switchFabIcon" format="reference"/>
 <attr name="switchFabColor" format="color"/>
 <attr name="animationDuration" format="integer"/>
 <attr name="animationMode"/>
 <attr name="position"/>
 </declare-styleable>

在构造器中我们同样是获取并初始化属性:

public MultiFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 //获取属性值
 getAttributes(context, attrs);
 //添加一个背景View和一个FloatingActionButton
 setBaseViews(context);
 }

private void getAttributes(Context context, AttributeSet attrs){
 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MultiFloatingActionButton);
 mBackgroundColor = typedArray.getColor(  R.styleable.MultiFloatingActionButton_backgroundColor, Color.TRANSPARENT);
 mFabIcon = typedArray.getDrawable(R.styleable.MultiFloatingActionButton_switchFabIcon);
 mFabColor = typedArray.getColorStateList(R.styleable.MultiFloatingActionButton_switchFabColor);
 mAnimationDuration = typedArray.getInt(R.styleable.MultiFloatingActionButton_animationDuration, 150);
 mAnimationMode = typedArray.getInt(R.styleable.MultiFloatingActionButton_animationMode, ANIM_SCALE);
 mPosition = typedArray.getInt(R.styleable.MultiFloatingActionButton_position, POS_RIGHT_BOTTOM);
 typedArray.recycle();
 }

接着我们初始化、添加蒙板和主fab。

private void setBaseViews(Context context){
 mBackgroundView = new View(context);
 mBackgroundView.setBackgroundColor(mBackgroundColor);
 mBackgroundView.setAlpha(0);
 addView(mBackgroundView);
 mFloatingActionButton = new FloatingActionButton(context);
 mFloatingActionButton.setBackgroundTintList(mFabColor);
 mFloatingActionButton.setImageDrawable(mFabIcon);
 addView(mFloatingActionButton);
 }

在onMeasure中,我们并不会对这个ViewGroup进行wrap_content的支持,因为基本上都是match_parent的吧,也不会有精确值,而且这个ViewGroup应该是在顶层的。我们看下onLayout方法,在这个方法中,我们对所有子View进行布局。

@Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
 if(changed){
  //布局背景和主Fab
  layoutFloatingActionButton();
  layoutBackgroundView();
  layoutItems();
 }
 }

首先布局主Fab,它在右下角,然后添加点击事件,点击这个主Fab后,会涉及到旋转主Fab,改变蒙板透明度,打开或关闭items等操作,这些等下再说。

private void layoutFloatingActionButton(){
 int width = mFloatingActionButton.getMeasuredWidth();
 int height = mFloatingActionButton.getMeasuredHeight();
 int fl = 0;
 int ft = 0;
 int fr = 0;
 int fb = 0;
 switch (mPosition){
  case POS_LEFT_BOTTOM:
  case POS_RIGHT_BOTTOM:
  fl = getMeasuredWidth() - width - dp2px(8);
  ft = getMeasuredHeight() - height - dp2px(8);
  fr = fl + width;
  fb = ft + height;
  break;
 }
 mFloatingActionButton.layout(fl, ft, fr, fb);
 bindFloatingEvent();
}
private void bindFloatingEvent(){
 mFloatingActionButton.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
  rotateFloatingButton();
  changeBackground();
  changeStatus();
  if (isMenuOpen) {
   openMenu();
  } else {
   closeMenu();
  }
  }
 });
 }

然后布局背景:

private void layoutBackgroundView(){
 mBackgroundView.layout(0, 0
  , getMeasuredWidth(), getMeasuredHeight());
 }

接着布局items,并为items添加点击事件。每个item都是TagFabLayout,可以为它setOnTagClickListener和setOnFabClickListener,以便我们点击这两块区域的时候都要能响应,并且我们让这两个回调函数中做同样的事情:旋转主Fab、改变背景、关闭items(因为能点击一定是展开状态)。此时还要在这个ViewGroup中设置一个接口OnFabItemClickListener,用于将点击的位置传递出去,例如Activity实现了这个接口,就可以在onTagClick和onFabClick方法中调用mOnFabItemClickListener.onFabItemClick()方法。说一下这里的布局,是累积向上的,注意坐标的计算。

private void layoutItems(){
 int count = getChildCount();
 for(int i=2; i<count; i++) {
  TagFabLayout child = (TagFabLayout) getChildAt(i);
  child.setVisibility(INVISIBLE);
  //获取自身测量宽高,这里说一下,由于TagFabLayout我们默认形成wrap_content,所以这里测量到的是wrap_content的最终大小
  int width = child.getMeasuredWidth();
  int height = child.getMeasuredHeight();
  // 获取主Fab测量宽高
  int fabHeight = mFloatingActionButton.getMeasuredHeight();
  int cl = 0;
  int ct = 0;
  switch (mPosition) {
  case POS_LEFT_BOTTOM:
  case POS_RIGHT_BOTTOM:
   cl = getMeasuredWidth() - width - dp2px(8);
   ct = getMeasuredHeight() - fabHeight - (i - 1) * height - dp2px(8);
  }
  child.layout(cl, ct, cl + width, ct + height);
  bindMenuEvents(child, i);
  prepareAnim(child);
 }
}
private void bindMenuEvents(final TagFabLayout child, final int pos){
 child.setOnTagClickListener(new TagFabLayout.OnTagClickListener() {
  @Override
  public void onTagClick() {
  rotateFloatingButton();
  changeBackground();
  changeStatus();
  closeMenu();
  if(mOnFabItemClickListener != null){
   mOnFabItemClickListener.onFabItemClick(child, pos);
  }
  }
 });
 child.setOnFabClickListener(new TagFabLayout.OnFabClickListener() {
  @Override
  public void onFabClick() {
  rotateFloatingButton();
  changeBackground();
  changeStatus();
  closeMenu();
  if (mOnFabItemClickListener != null){
   mOnFabItemClickListener.onFabItemClick(child, pos);
  }
  }
 });
}

现在所有的布局和点击事件都已经绑定好了,我们来看下rotateFloatingButton()、 changeBackground() 、 openMenu() 、closeMenu()这几个和属性动画相关的函数。

其实也很简单,rotateFloatingButton()对mFloatingActionButton的rotation这个属性进行改变,以菜单是否打开为判断条件。

private void rotateFloatingButton(){
 ObjectAnimator animator = isMenuOpen ? ObjectAnimator.ofFloat(mFloatingActionButton
  , "rotation", 45F, 0f) : ObjectAnimator.ofFloat(mFloatingActionButton, "rotation", 0f, 45f);
 animator.setDuration(150);
 animator.setInterpolator(new LinearInterpolator());
 animator.start();
 }

changeBackground()改变mBackgroundView的alpha这个属性,也是以菜单是否打开为判断条件。

private void changeBackground(){
 ObjectAnimator animator = isMenuOpen ? ObjectAnimator.ofFloat(mBackgroundView, "alpha", 0.9f, 0f) :
 ObjectAnimator.ofFloat(mBackgroundView, "alpha", 0f, 0.9f);
 animator.setDuration(150);
 animator.setInterpolator(new LinearInterpolator());
 animator.start();
 }

openMenu() 中根据不同的模式来实现打开的效果,看一下scaleToShow(),这里同时对scaleX、scaleY、alpha这3个属性进行动画,来达到放大显示的效果。

private void openMenu(){
 switch (mAnimationMode){
  case ANIM_BOUNCE:
  bounceToShow();
  break;
  case ANIM_SCALE:
  scaleToShow();
 }
 }
private void scaleToShow(){
 for(int i = 2; i<getChildCount(); i++){
  View view = getChildAt(i);
  view.setVisibility(VISIBLE);
  view.setAlpha(0);
  ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f);
  ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f);
  ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
  AnimatorSet set = new AnimatorSet();
  set.playTogether(scaleX, scaleY, alpha);
  set.setDuration(mAnimationDuration);
  set.start();
 }
}

差不多达到我们要求的效果了,但是还有一个小地方需要注意一下,在menu展开的时候,如果我们点击menu以外的区域,即蒙板上的区域,此时ViewGroup是不会拦截任何Touch事件,如果在这个FloatingActionButton下面有可以被点击响应的View,比如ListView,就会在蒙板显示的情况下进行响应,正确的逻辑应该是关闭menu。

那么我们需要在onInterceptTouchEvent中处理事件的拦截,这里判断的方法是:如果menu是打开的,我们在DOWN事件中判断x,y是否落在了a或b区域,如下图

如果是的话,该ViewGroup应该拦截这个事件,交由自身的onTouchEvent处理。

@Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
 boolean intercepted = false;
 int x = (int)ev.getX();
 int y = (int)ev.getY();
 if(isMenuOpen){
  switch (ev.getAction()){
  case MotionEvent.ACTION_DOWN:
   if(judgeIfTouchBackground(x, y)){
   intercepted = true;
   }
   intercepted = false;
   break;
  case MotionEvent.ACTION_MOVE:
   intercepted = false;
   break;
  case MotionEvent.ACTION_UP:
   intercepted = false;
   break;
  }
 }
 return intercepted;
 }
 private boolean judgeIfTouchBackground(int x, int y){
  Rect a = new Rect();
  Rect b = new Rect();
  a.set(0, 0, getWidth(), getHeight() - getChildAt(getChildCount() - 1).getTop());
  b.set(0, getChildAt(getChildCount() - 1).getTop(), getChildAt(getChildCount() - 1).getLeft(), getHeight());
  if(a.contains(x, y) || b.contains(x, y)){
  return true;
  }
  return false;
 }

在onTouchEvent中做关闭menu等操作。

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 if(isMenuOpen){
  closeMenu();
  changeBackground();
  rotateFloatingButton();
  changeStatus();
  return true;
 }
 return super.onTouchEvent(event);
 }

再看一下,效果不错。

由于我做的小app中涉及到切换夜间模式,这个ViewGroup的背景色应该随着主题改变,设置该View的背景色为

app:backgroundColor="?attr/myBackground"

重写ViewGroup的 setBackgroundColor方法,这里所谓的背景色其实就是蒙板的颜色。

public void setBackgroundColor(int color){
 mBackgroundColor = color;
 mBackgroundView.setBackgroundColor(color);
}

基本功能到这里全部完成了,问题还有很多,比如没有提供根据不同的position进行布局、没有提供根据不同mode设置menu开闭的效果,但是后续我还会继续改进和完善^ ^。欢迎交流。如果大家需要源码,可以去我源码里的customview里面自取。在这里

以上所述是小编给大家介绍的Android仿知乎悬浮功能按钮FloatingActionButton效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android开发设置RadioButton点击效果的方法

    本文实例讲述了Android开发设置RadioButton点击效果的方法.分享给大家供大家参考,具体如下: 在安卓开发中用到底部菜单栏 需要用到RadioButton这个组件 实际应用的过程中,需要对按钮进行点击,为了让用户知道是否点击可这个按钮,可以设置点击后 ,该按钮的颜色或者背景发生变化. layout中这部分的代码为: <RadioButton android:id="@+id/radio_button0" android:layout_height="fill

  • Android Button 自带阴影效果另一种解决办法

    在Android 5.0以后的版本中,定义一个button时,系统自动会加一个阴影的效果,有的时候这种效果看起来比较好,有的时候不符合UI的设计要求,这时候就需要手动去掉阴影. 网上很多文章写了解决办法,就是给button加一句话style="?android:attr/borderlessButtonStyle",这个确实能解决问题,但是又带来了另外一个问题,就是一般情况下,在写布局的时候,都会给每个控件写一个style,这样方便复用,比如我写了一个button,引了一个style,

  • Android setButtonDrawable()的兼容问题解决办法

    Android  setButtonDrawable()的兼容问题解决办法 setButtonDrawable()的兼容问题 API16实现 /** * Set the background to a given Drawable, identified by its resource id. * * @param resid the resource id of the drawable to use as the background */ public void setButtonDraw

  • Android ButtonOnClick事件的写法总结

    Android ButtonOnClick事件的写法总结 假设layout里有三个Button吧,id分别是 button_1 ,button_2 , button_3 之前一直都知道有两种onClick写法: button_1.setOnClickListener(new Button.OnClickListener(){ public void onClick(View v) { //在这里添加点击事件 } }); 第二种: button_2.setOnClickListener(liste

  • Android悬浮按钮点击返回顶部FloatingActionButton

    先看一下Android悬浮按钮点击回到顶部的效果: FloatingActionButton是Design Support库中提供的一个控件,这个控件可以轻松实现悬浮按钮的效果 首先,要在项目中使用这个悬浮按钮就要先把design这个包导入项目 gradle中加入依赖 compile 'com.android.support:design:25.0.0' 接下来就是在xml中使用: 我这里是放置一个listView模拟返回顶部 <?xml version="1.0" encodi

  • Android编程实现自定义PopupMenu样式示例【显示图标与设置RadioButton图标】

    本文实例讲述了Android编程实现自定义PopupMenu样式.分享给大家供大家参考,具体如下: PopupMenu是Android中一个十分轻量级的组件.与PopupWindow相比,PopupMenu的可自定义的能力较小,但使用更加方便. 先上效果图: 本例要实现的功能如下: 1.强制显示菜单项的图标. 默认状态下,PopupMenu的图标是不显示的,并且Android没有为我们开放任何API去设置它的显示状态.为了显示菜单项的图标,可以自己重写PopupMenu并修改相关属性,也可以直接

  • Android中FloatingActionButton实现悬浮按钮实例

    Android中FloatingActionButton(悬浮按钮) 使用不是特别多,常规性APP应用中很少使用该控件. 当然他的使用方法其实很简单.直接上代码: xml: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="

  • Android仿知乎悬浮功能按钮FloatingActionButton效果

    前段时间在看属性动画,恰巧这个按钮的效果可以用属性动画实现,所以就来实践实践.效果基本出来了,大家可以自己去完善. 首先看一下效果图: 我们看到点击FloatingActionButton后会展开一些item,然后会有一个蒙板效果,这都是这个View的功能.那么这整个View肯定是个ViewGroup,我们一部分一部分来看. 首先是这个最小的Tag: 这个Tag带文字,可以是一个TextView,但为了美观,我们使用CardView,CardView是一个FrameLayout,我们要让它具有显

  • Android仿知乎客户端关注和取消关注的按钮点击特效实现思路详解

    先说明一下,项目代码已上传至github,不想看长篇大论的也可以先去下代码,对照代码,哪里不懂点哪里. 代码在这https://github.com/zgzczzw/ZHFollowButton 前几天发现知乎关注的点击效果确实赞,查了一下实现方式,刚好看到这个问题,花了一天时间终于把这个效果实现了,现在来回答一下,很不幸,楼上各位的答案都不全对,且听我一一道来. 首先,我先详细观察了一些知乎的效果,其中有一个很神奇的地方,如图: 注意看第二张图,这个圆形在扩散的时候,圆形底下的字还在,而且新的

  • Android仿微信文章悬浮窗效果的实现代码

    序言 前些日子跟朋友聊天,朋友Z果粉,前些天更新了微信,说微信出了个好方便的功能啊,我问是啥功能啊,看看我大Android有没有,他说现在阅读公众号文章如果有人给你发微信你可以把这篇文章当作悬浮窗悬浮起来,方便你聊完天不用找继续阅读,听完是不是觉得这叫啥啊,我大Android微信版不是早就有这个功能了吗,我看文章的时候看到过有这个悬浮按钮,但是我一直没有使用过,试了一下还是挺方便的,就想着自己实现一下这个功能,下面看图,大家都习惯了无图言X 原理 看完动图我们来分析一下,如何在每个页面上都存在一

  • android仿华为手机悬浮窗设计

    本文实例为大家分享了android仿华为手机悬浮窗的具体代码,供大家参考,具体内容如下 最近项目中有个需求就是要在android 系统桌面上写一个悬浮球,并使其具有返回,进到主页,打开设置等功能.类似于华为手机的悬浮球.这里主要用到windowManager来实现. 1.先来看看效果图 主页的小圆点 点击小圆点之后展开,然后可以模拟虚拟按键,返回等功能.全局有效. 2.一步步来实现 1.首先这个要常住在桌面,故得写在一个服务里面里面.服务的启动可以通过开机广播,或者在Activity 中启动后直

  • Android仿微信语音聊天功能

    本文实例讲述了Android仿微信语音聊天功能代码.分享给大家供大家参考.具体如下: 项目效果如下: 具体代码如下: AudioManager.java package com.xuliugen.weichat; import java.io.File; import java.io.IOException; import java.util.UUID; import android.media.MediaRecorder; public class AudioManager { private

  • android 仿微信demo——登录功能实现(移动端)

    移动端登录功能实现 登录功能基本和注册一样,唯一不同的是登录可以实现两种登录方式(微信号和手机号),也就是布局不一样.所以需要两个布局,两个activity(这个方法比较简单粗暴,我懒.也可以通过activity动态切换布局,这样只需要一个activity就可以了) 创建两个activity,实现两种登录方式 微信号登录activity LoginUser.java package com.example.wxchatdemo; import android.annotation.Suppres

  • Android仿音乐播放器功能

    本文实例为大家分享了Android仿音乐播放器功能的具体代码,供大家参考,具体内容如下 读取本地音乐文件 源代码: import android.media.MediaPlayer; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.ImageButton; import android.widget.

  • Android 仿微博的点赞功能的实现原理(持续点赞再取消)

    产品需求,实现类似微博的持续点赞再取消功能,因为自己也偶尔刷微博,对这功能有一定的使用上的了解, 至于微博点赞的具体实现我并不知道,微博点赞在断网的情况下依然能点赞,不会提示网络异常,等有网络之后 重新刷新,实际是没有点赞的,那就针对这现象去实现吧. 避免并发,减少CPU压力,我个人会想到 HandlerThread ,不懂可以自行科普,这里只说我实现的点赞功能原理. private Timer mTimer;//定时器 private TimerTask mTask; mMap = new H

  • Android仿简书长按文章生成图片效果

    前言 使用简书APP的同学都知道,简书有这样一个功能:文章页长按内容时底部会出现一个 生成图片分享 的按钮,点击之后就可以将当前的文章生成一张长图片:这张图片可以保存到本地或分享给好友,同时还可为图片设置成为白和黑两种风格,很有艺术范.个人一直很喜欢这个功能. 但是从某一个版本开始,这个功能开始有bug了,生成的图片只有底部的固定标题,而没有文章内容,长图也变成了小短图.向简书意见反馈后,得到的回复是,使用点击分享按钮生成图片功能:分享菜单包含的生成长图功能的确是可以的.但是,还是很怀念之前长按

  • Android仿QQ空间顶部条背景变化效果

    本文给大家分享仿QQ空间页面顶部条随界面滑动背景透明度变化的效果,这个效果在其他应用程序中也很常见,技能+1. 一.上代码,具体实现 笔者之前的文章第二部分总是二话不说,直接上代码,很干脆,其实更好的方式是引导读者思考:这个效果如何实现.前期做好效果的功能分析,才能读者更好的理解. QQ空间的这个页面其实并不复杂,我们看看QQ空间的演示界面: 可以看见,整个页面其实只有两个根元素,一个是ListView,一个是标题栏,前者可以上下滑动,给用户呈现内容:后者固定位置不动,类似于一个导航栏,左边一个

随机推荐