我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来。近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效果。于是花点时间动手试了试,基本上达到了QQ空间的效果,截图如下:

        通过观察QQ空间的运行效果,发现当往上滚动时菜单栏会随着滚动距离的增大其透明度组件增大直到完全不透明,反之逐渐透明。当滚动到顶部后继续下拉会出现拉升效果当松手之后出现阻尼回弹效果。于是就通过重写ListView模仿了QQ空间的运行效果。
实现QQ空间运行效果前需要考虑两个问题:
1)、如何实现菜单栏透明度渐变
        通过观察QQ空间的运行效果可知其菜单栏的透明度是根据滚动距离而动态变化的,要想实现透明度的变化就需要知道的ListView的滚动距离,所以有关透明度的问题也就转化成了滚动距离的问题。
2)、如何实现阻尼拉升和回弹效果
        要想利用ListView实现阻尼效果就要求ListView首先滚动到了顶部,当ListView滚动到了顶部之后若继续手动下滑就要求其第一个Child变化来模拟下拉效果,当手指松开后该Child要回弹到初始状态。
        我们先看第一个问题:要想实现透明度渐变就要先获取到ListView的滚动距离,通过滚动距离来计算相应的透明度。由于ListView的复用机制就决定了不能通过第一个可见Item的getTop()方法来得到滚动值,所以我们可以通过HeaderView来获取滚动距离,因为Header在ListView中是不参与复用的。
        下面先了解一下ListView添加HeaderView后的滚动流程:

上图大致画了ListView含有HeaderView时的三个滚动状态,状态一可称为初始状态或者是恰好滚动到最顶部状态,此时HeaderView的getTop()值为0;状态二为ListView的滚动中状态,此时HeaderView没有完全滚动出ListView边界,getTop()的返回值为负数且其绝对值范围在0和HeaderView的高度之间;状态三表示的是HeaderView完全滚动出了ListView边界,若调用getTop()得到的返回值为负数且绝对值等于HeaderView的高度(此后可理解成HeaderView一直固定在ListView的顶部)。
        明白了ListView的滚动原理,我们先尝试实现渐变菜单栏的功能。首先定义自己的ListView,取名为FlexibleListView,单词flexible是灵活的、多样的的意思,因为我们的ListView不仅要实现菜单栏的透明度渐变还要实现阻尼效果,所以取名为FlexibleListView比较恰当。FlexibleListView继承ListView后需要实现其构造方法,代码如下:


public class FlexibleListView extends ListView { 
 public FlexibleListView(Context context) { 
 super(context); 
 } 
 public FlexibleListView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) { 
 super(context, attrs, defStyleAttr); 
 } 
 @TargetApi(21) 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
 super(context, attrs, defStyleAttr, defStyleRes); 
 } 
} 

        FlexibleListView仅仅是继承了ListView,这本质上和ListView没有区别。既然我们是通过给ListView添加HeaderView的方式来判断滚动距离,那就要获取到HeaderView对象。怎么获取到HeaderView对象呢?这里有个技巧,由于给ListView添加HeaderView最终是调用ListView的addHeaderView(View v, Object data, boolean isSelected)方法,所以我们可以重写该方法,取到添加进来的第一个HeaderView,那怎么判断是第一个添加进来的HeaderView呢?因为HeaderView的添加是有序的即先添加的先绘制。所以可以定义一个代表第一个HeaderView的属性mHeaderView,当调用到addHeaderView()方法时通过判断mHeaderView的值是否为空,如果为空就赋值否则不赋值,代码如下:


public class FlexibleListView extends ListView { 
 private View mHeaderView; 
 private int mMaxScrollHeight; 
 public FlexibleListView(Context context) { 
 super(context); 
 } 
 public FlexibleListView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) { 
 super(context, attrs, defStyleAttr); 
 } 
 @TargetApi(21) 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
 super(context, attrs, defStyleAttr, defStyleRes); 
 } 
 @Override 
 public void addHeaderView(View v, Object data, boolean isSelectable) { 
 super.addHeaderView(v, data, isSelectable); 
 if(null == mHeaderView) { 
 mHeaderView = v; 
 mMaxScrollHeight = mHeaderView.getLayoutParams().height; 
 } 
 } 
} 

         FlexibleListView中定义了mHeaderView和mMaxScrollHeight属性,在addHeaderView()方法中对mHeaderView做非空判断来获取到第一个HeaderView并赋值给mHeadereView,mMaxScrollHeight表示HeaderView的最大滚动距离,当HeaderView的滚动距离超过此值我们就要设置菜单栏不透明否则就更改透明度。在这里我直接使用了HeaderView的高度来表示其允许滚动的最大距离。
        现在可以获取到ListView的第一个HeaderView,接下来就是判断ListView的滚动了,这时候有的童靴可能会想到采用给ListView添加ScrollListener的方式,这种方式是可行的,但我们这次不采用添加Listener的方式,如果你对ListView的源码比较熟悉的话就清楚触发OnItemScrollListener的回调时机是在AbsListView的invokeOnItemScrollListener()方法中,该方法源码如下:


 
void invokeOnItemScrollListener() { 
 if (mFastScroll != null) { 
 mFastScroll.onScroll(mFirstPosition, getChildCount(), mItemCount); 
 } 
 if (mOnScrollListener != null) { 
 mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount); 
 } 
 onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these. 
} 

        invokeOnItemScrollListener()方法就是触发滚动回调的,无论我们给不给ListView设置OnItemScrollListener那该方法都会调用,细心的同学可能发现在该方法最后调用了View的onScrollChanged()方法,这时候你恍然大悟,我们可以重写该方法呀,当ListView发生滚动了也就调用了onScrollChange()方法,多省事呀。呵呵,恭喜你,答对了,我们今天就是采用重写onScrollChanged()方法并在该方法中通过判断ListView的HeaderView的滚动距离来设置菜单栏的透明度的。
        现在我们清楚了ListView的滚动时机,也有了HeaderView和最大滚动距离,接下来就是分析实现渐变的条件了:要实现渐变我们就要清楚是谁要渐变,在我们的APP中可能是ActionBar,也可能是ToolBar,还有可能是我们自定义的一个ViewGroup来模拟的ActionBar,所以FlexibleListView得有个代表ActionBar的mActionBar属性并对外提供一个方法bindActionBar(),该方法就表示把需要实现渐变的ActionBar传递进来,代码如下:


public class FlexibleListView extends ListView { 
 private View mActionBar; 
 private View mHeaderView; 
 private int mMaxScrollHeight; 
 private Drawable mActionBarBackground; 
 public FlexibleListView(Context context) { 
 super(context); 
 } 
 public FlexibleListView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) { 
 super(context, attrs, defStyleAttr); 
 } 
 @TargetApi(21) 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
 super(context, attrs, defStyleAttr, defStyleRes); 
 } 
 @Override 
 protected void onScrollChanged(int l, int t, int oldl, int oldt) { 
 super.onScrollChanged(l, t, oldl, oldt); 
 if(null != mActionBarBackground) { 
 mActionBarBackground.setAlpha(evaluateAlpha(Math.abs(mHeaderView.getTop()))); 
 } 
 } 
 @Override 
 public void addHeaderView(View v, Object data, boolean isSelectable) { 
 super.addHeaderView(v, data, isSelectable); 
 if(null == mHeaderView) { 
 mHeaderView = v; 
 mMaxScrollHeight = mHeaderView.getLayoutParams().height; 
 } 
 } 
 private int evaluateAlpha(int t) { 
 if (t >= mMaxScrollHeight) { 
 return 255; 
 } 
 return (int) (255 * t /(float) mMaxScrollHeight); 
 } 
 public void bindActionBar(View actionBar) { 
 if(null != actionBar) { 
 mActionBar = actionBar; 
 mActionBarBackground = actionBar.getBackground(); 
 if(null == mActionBarBackground) { 
 mActionBarBackground = new ColorDrawable(Color.TRANSPARENT); 
 } 
 mActionBarBackground.setAlpha(0); 
 if(Build.VERSION.SDK_INT >= 16) { 
 mActionBar.setBackground(mActionBarBackground); 
 } else { 
 mActionBar.setBackgroundDrawable(mActionBarBackground); 
 } 
 } 
 } 
 public void bindActionBar(ActionBar actionBar) { 
 if(null != actionBar) { 
 // TODO impl with ActionBar 
 // actionBar.setBackgroundDrawable(); 
 } 
 } 
} 

        FlexibleListView新增了mActionBar和mActionBarBackground属性,mActionBar代表需要渐变的菜单栏,mActionBarBackground为菜单栏的背景。其次对外提供了重载方法bindActionBar(),参数为ActionBar的方法是空实现,里边添加了TODO提示符并给了setBackgroundDrawable()提示(注意ActionBar实现渐变需要设置WindowFeature),希望童靴们自己可以实现出来。
        FlexibleListView中重写了onScrollChanged()方法,在该方法中通过获取mHeaderView的getTop()值然后调用evaluateAlpha()方法计算出alpha值,evaluateAlpha()的计算很简单,当滚动值超过了最大滚动距离mMaxScrollHeight就返回255(255表示不透明,0表示透明),否则计算出当前滚动值所对应的alpha值,最后通过调用mActionBarBackground的setAlpha()来达到mActionBar的透明度变化。
        现在实现菜单栏的透明度的逻辑准备就绪了,我们先测试一下看看,定义菜单栏布局,代码如下:


<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="@dimen/action_bar_height" 
 android:background="#aabbcc" 
 android:clickable="true" 
 android:orientation="vertical" 
 android:paddingLeft="10dp"> 
 <TextView 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_gravity="center_vertical" 
 android:drawableLeft="@mipmap/back" 
 android:text="动态" 
 android:textColor="#b8e7fe" 
 android:textSize="17sp" /> 
 <TextView 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_gravity="center" 
 android:text="好友动态" 
 android:textColor="#b8e7fe" 
 android:textSize="17sp" /> 
</FrameLayout>

          菜单栏包含一个返回按钮和一个标题,并且给菜单栏设置了固定高度和背景色,然后布局我们的activity_main.xml文件,代码如下:


<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout 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"> 
 <com.llew.wb.git.qqzone.FlexibleListView 
 android:id="@+id/flexible_list_view" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:scrollbars="none"></com.llew.wb.git.qqzone.FlexibleListView> 
 <include 
 android:id="@+id/custom_action_bar" 
 layout="@layout/action_bar_layout"/> 
</FrameLayout> 

        activity_main.xml的布局文件很简单,采用FrameLayout根布局让菜单栏悬浮在FlexibleListView上边,然后编写我们的MainActivity代码,如下所示:


public class MainActivity extends AppCompatActivity { 
 private FlexibleListView mListView; 
 @Override 
 protected void onCreate(Bundle savedInstanceState) { 
 super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_main); 
 initGlobalParams(); 
 } 
 private void initGlobalParams() { 
 mListView = (FlexibleListView) findViewById(R.id.flexible_list_view); 
 View mFlexibleHeaderView = new View(getApplicationContext()); 
 mFlexibleHeaderView.setBackgroundColor(Color.parseColor("#bbaacc")); 
 int height = getResources().getDimensionPixelSize(R.dimen.header_height); 
 LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, height); 
 mFlexibleHeaderView.setLayoutParams(params); 
 final View actionBar = findViewById(R.id.custom_action_bar); 
 mListView.bindActionBar(actionBar); 
 mListView.addHeaderView(mFlexibleHeaderView); 
 mListView.setAdapter(new Adapter()); 
 } 
 static class Adapter extends BaseAdapter { 
 @Override 
 public int getCount() { 
 return 80; 
 } 
 @Override 
 public Object getItem(int position) { 
 return null; 
 } 
 @Override 
 public long getItemId(int position) { 
 return 0; 
 } 
 @Override 
 public View getView(int position, View convertView, ViewGroup parent) { 
 TextView textView = new TextView(parent.getContext()); 
 textView.setPadding(50, 50, 50, 50); 
 textView.setText(position + 10 + ""); 
 return textView; 
 } 
 } 
} 

        在MainActivity中给FlexibleListView添加了一个固定高度背景色为"#bbaacc"的Header,并把悬浮菜单栏actionBar赋值给了FlexibleListView的mActionBar,最后设置Adapter,为了测试代码写的很简单,我们运行一下程序,看看效果:

        看到运行效果好开心呀,(*^__^*) ……透明度渐变功能达到了我们的预期,接下来开始实现阻尼效果,阻尼效果就是当ListView滚动到了顶部此时若继续下滑,ListView能够继续往下滚动一段距离当手指离开屏幕后ListView要恢复原位置。为了实现这个功能有的童靴可能会想到重写有关事件传递的onXXXEvent()等方法,之后在MotionEvent为DOWN,MOVE,UP或者CANCEL条件下分别做逻辑判断来实现阻尼效果,此方式可行,但是和今天我们的实现相比起来复杂了许多......
        这里所实现阻尼效果所采用的方法是利用View的overScrollBy()方法,有的童靴可能会问overScrollBy()方法是2.3版本之后才增加的,2.3版本之前的兼容性怎么办?我实现这个功能之前也考虑过这个问题,一方面我们公司的APP只支持3.0以上版本,另一方面2.3及以前的版本市场占有率几乎微乎其微了,所以可以考虑不再兼容2.3以前的老版本。
        有的同学或许对overScrollBy()方法比较陌生,先大致说一下该方法,其源码如下:


 
@SuppressWarnings({"UnusedParameters"}) 
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, 
 int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 
 // ...... 
} 

        阅读源码看注释很重要,我们先看一下注释,大致意思如下:
        当View组件滚动到边界时还会继续进行之前的滚动操作(注意:没有滚动到边界时是不会触发该方法的),如果View组件调用了该方法那么View组件就应该重写onOverScrolled()方法来响应over-scroll操作。View控件可以调用该方法处理任何的触摸滚动或者是快速滑动等。感觉翻译的好别扭,说的直白点就是当ListView,ScrollView等滚动到头了若继续下滑就会调用该方法。
        overScrollBy()方法有9个参数,每个参数注释都说的很详细,我们只看需要用到的俩参数deltaY和isTouchEvent;deltaY表示的是在Y轴上滚动的相对值,比如ListView滚动到了顶部此时如果继续下拉,deltaY值为负数,当其滚动到了最底部当我们继续上拉,deltaY值为正数,所以我们可以根据deltaY判断ListView是上拉操作还是下拉操作,isTouchEvent为true表示手指在触摸屏幕否则离开屏幕。
        了解overScrollBy()方法后开始实现阻尼效果,核心就是重写overScrollBy()方法,在该方法中动态改变HeaderView的高度,若手指松开我们就复原HeaderView。我们知道QQ空间顶部是一张图片,当下拉的时候该图片有弹性拉升效果,当手指松开后图片又伸缩回去了,所以我们就直接用ImageView模拟此效果。模拟图片阻尼可以让ImageView的宽高为MATCH_PARENT(HeaderView的高度改变之后ImageView的高度也可以随之更改),这个时候还要设置ImageView的scaleType为CENTER_CROP(不清楚ImageView的scaleType属性可参照我之前写的一篇博文:Android 源码系列之<一>从源码的角度深入理解ImageView的ScaleType属性)。
        现在开始在FlexibleListView中重写overScrollBy()方法,代码如下:


@Override 
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 
 if(null != mHeaderView) { 
 if(isTouchEvent && deltaY < 0) { 
 mHeaderView.getLayoutParams().height += Math.abs(deltaY / 3.0); 
 mHeaderView.requestLayout(); 
 } 
 } 
 return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 
} 

        overScrollBy()方法中我们根据deltaY值动态的更改了mHeaderView的高度并重新布局达到更改ImageView高度的目的,注意:计算高度的时候用了deltaY除以3,此时的3表示增长因子,目的是让HeaderView缓慢的增长,这里可以对外提供一个方法来设置此值。
        现在仅实现了HeaderView的拉升功能,但是还没有实现缩放功能,因为overScrollBay()中实现的是手指触摸的下拉,当手指离开屏幕后要进行HeaderView的复原操作,所以我们可以在考虑在onTouchEvent()方法中判断MotionEvent的类型,当为UP或者CANCEL时就复原HeaderView,复原HeaderView不能一下子复原而是要用动画的方式,这样看上去才比较自然,所以onTouchEvent()代码如下:


@Override 
public boolean onTouchEvent(MotionEvent ev) { 
 if(null != mHeaderView) { 
 int action = ev.getAction(); 
 if(MotionEvent.ACTION_UP == action || MotionEvent.ACTION_CANCEL == action) { 
 resetHeaderViewHeight(); 
 } 
 } 
 return super.onTouchEvent(ev); 
} 
private void resetHeaderViewHeight() { 
 ValueAnimator valueAnimator = ValueAnimator.ofInt(1); 
 valueAnimator.setDuration(700); 
 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
 @Override 
 public void onAnimationUpdate(ValueAnimator animation) { 
 final float f = animation.getAnimatedFraction(); 
 mHeaderView.getLayoutParams().height -= f * (mHeaderView.getLayoutParams().height - mMaxScrollHeight); 
 mHeaderView.requestLayout(); 
 } 
 }); 
 valueAnimator.setInterpolator(new OvershootInterpolator()); 
 valueAnimator.start(); 
} 

        HeaderView的复原动画我们采用了ValueAnimator,当动画执行过程中我们动态的更改HeaderView的值来达到渐变效果。接下来布局HeaderView来模拟QQ空间,代码如下:


<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="@dimen/header_height"> 
 <ImageView 
 android:id="@+id/iv" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:scaleType="centerCrop" 
 android:class="lazy" data-src="@mipmap/ttt" /> 
 <LinearLayout 
 android:layout_width="match_parent" 
 android:layout_height="30dp" 
 android:layout_gravity="bottom" 
 android:background="#33333333" 
 android:gravity="center_vertical" 
 android:orientation="horizontal"> 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="相册" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 <View 
 android:layout_width="1dp" 
 android:layout_height="20dp" 
 android:background="#ffffff" /> 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="说说" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 <View 
 android:layout_width="1dp" 
 android:layout_height="20dp" 
 android:background="#ffffff" /> 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="个性化" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 <View 
 android:layout_width="1dp" 
 android:layout_height="20dp" 
 android:background="#ffffff" /> 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="\@ 与我相关" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 </LinearLayout> 
</FrameLayout> 

        HeaderView的布局中让ImageView的宽高都设置成了match_parent并且把scaleType设置为centerCrop。修改MainActivity的initGlobalParams()方法,代码如下:


void initGlobalParams() { 
 mListView = (FlexibleListView) findViewById(R.id.flexible_list_view); 
 View mFlexibleHeaderView = LayoutInflater.from(this).inflate(R.layout.flexible_header_layout, mListView, false); 
 AbsListView.LayoutParams params = (AbsListView.LayoutParams)mFlexibleHeaderView.getLayoutParams(); 
 if(null == params) { 
 params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT); 
 } 
 params.height = getResources().getDimensionPixelSize(R.dimen.header_height); 
 mFlexibleHeaderView.setLayoutParams(params); 
 final View actionBar = findViewById(R.id.custom_action_bar); 
 mListView.bindActionBar(actionBar); 
 mListView.addHeaderView(mFlexibleHeaderView); 
 mListView.setAdapter(new Adapter()); 
} 

        OK,一切都准备就绪,赶紧运行一下程序,看看效果吧(*^__^*) ……

 恩,看上去效果还不错......
 好了,有关实现QQ空间的阻尼下拉刷新和渐变菜单栏就结束了,主要是利用了2.3版本之后的overScrollBy()方法(如果要兼容2.3之前版本需要童靴们自己去实现相关逻辑);其次充分的利用了ImageView的ScaleType属性来模拟了QQ空间图片阻尼回弹的效果。再次感谢收看(*^__^*) ……

原文链接:http://blog.csdn.net/llew2011/article/details/51559694

您可能感兴趣的文章:android自定义进度条渐变色View的实例代码Android ScrollView滑动实现仿QQ空间标题栏渐变Android中Toolbar随着ScrollView滑动透明度渐变效果实现Android ListView滑动改变标题栏背景渐变效果Android自定义渐变式炫酷ListView下拉刷新动画Android Textview实现颜色渐变滚动效果Android中recyclerView底部添加透明渐变效果Android自定义View实现渐变色进度条Android开发之ListView的head消失页面导航栏的渐变出现和隐藏Android自定义view渐变圆形动画


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录