转载请注明出处:http://blog.csdn.net/llew2011/article/details/51559694

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

通过观察QQ空间的运行效果,发现当往上滚动时菜单栏会随着滚动距离的增大其透明度组件增大直到完全不透明,反之逐渐透明。当滚动到顶部后继续下拉会出现拉升效果当松手之后出现阻尼回弹效果。于是就通过重写ListView模仿了QQ空间的运行效果。

实现QQ空间运行效果前需要考虑两个问题:

  • 如何实现菜单栏透明度渐变
            通过观察QQ空间的运行效果可知其菜单栏的透明度是根据滚动距离而动态变化的,要想实现透明度的变化就需要知道的ListView的滚动距离,所以有关透明度的问题也就转化成了滚动距离的问题。
  • 如何实现阻尼拉升和回弹效果
            要想利用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);}@Overridepublic 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()方法中,该方法源码如下:

/*** Notify our scroll listener (if there is one) of a change in scroll state*/
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);}@Overrideprotected 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())));}}@Overridepublic 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"><TextViewandroid: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" /><TextViewandroid: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.FlexibleListViewandroid:id="@+id/flexible_list_view"android:layout_width="match_parent"android:layout_height="match_parent"android:scrollbars="none"></com.llew.wb.git.qqzone.FlexibleListView><includeandroid: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;@Overrideprotected 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 {@Overridepublic int getCount() {return 80;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic 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()方法比较陌生,先大致说一下该方法,其源码如下:

/*** Scroll the view with standard behavior for scrolling beyond the normal* content boundaries. Views that call this method should override* {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the* results of an over-scroll operation.** Views can use this method to handle any touch or fling-based scrolling.** @param deltaX Change in X in pixels* @param deltaY Change in Y in pixels* @param scrollX Current X scroll value in pixels before applying deltaX* @param scrollY Current Y scroll value in pixels before applying deltaY* @param scrollRangeX Maximum content scroll range along the X axis* @param scrollRangeY Maximum content scroll range along the Y axis* @param maxOverScrollX Number of pixels to overscroll by in either direction*          along the X axis.* @param maxOverScrollY Number of pixels to overscroll by in either direction*          along the Y axis.* @param isTouchEvent true if this scroll operation is the result of a touch event.* @return true if scrolling was clamped to an over-scroll boundary along either*          axis, false otherwise.*/
@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() {@Overridepublic 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"><ImageViewandroid:id="@+id/iv"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/ttt" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="30dp"android:layout_gravity="bottom"android:background="#33333333"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:text="相册"android:gravity="center"android:textColor="@android:color/white" /><Viewandroid:layout_width="1dp"android:layout_height="20dp"android:background="#ffffff" /><TextViewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:text="说说"android:gravity="center"android:textColor="@android:color/white" /><Viewandroid:layout_width="1dp"android:layout_height="20dp"android:background="#ffffff" /><TextViewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:text="个性化"android:gravity="center"android:textColor="@android:color/white" /><Viewandroid:layout_width="1dp"android:layout_height="20dp"android:background="#ffffff" /><TextViewandroid: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空间图片阻尼回弹的效果。在下篇文章Android UI设计之<十三>自定义ScrollView,实现QQ空间阻尼下拉刷新和渐变菜单栏效果中我又通过自定义ScrollView的方式实现了同样效果的文章,如果你有需要,请点击这里,最后感谢收看(*^__^*) ……

源码下载

Android UI设计之十自定义ListView,实现QQ空间阻尼下拉刷新和渐变菜单栏效果相关推荐

  1. android仿qq动态视频播放,Android 自定义ListView实现QQ空间界面(说说内包含图片、视频、点赞、评论、转发功能)...

    前端时间刚好需要做一个类似于QQ空间的社区分享功能,说说内容包含文字(话题.内容).视频.图片,还需包含点赞,评论,位置信息等功能. 就采用LIstview做了一个,先来看下效果,GIF太大,CSDN ...

  2. android自带下拉阻尼动画,android 有阻尼下拉刷新列表的实现方法

    本文将会介绍有阻尼下拉刷新列表的实现,先来看看效果预览: 这是下拉状态: 这是下拉松开手指后listView回滚到刷新状态时的样子: 1. 如何调用 虽然效果图看起来样子不太好看,主要是因为那个蓝色的 ...

  3. android抖音自动刷新,Android 使用SwipeRefreshLayout控件仿抖音做的视频下拉刷新效果...

    SwipeRefreshLayout(这个控件),我先跟大家介绍一下这个控件: 一.SwipeRefreshLayout简单介绍 •先看以下官方文档,已有了很详细的描述了. 官方文档说明 •这里我再大 ...

  4. Android UI设计之十一自定义ViewGroup,打造通用的关闭键盘小控件ImeObser

    2019独角兽企业重金招聘Python工程师标准>>> 转载请注明出处:http://blog.csdn.net/llew2011/article/details/51598682 ...

  5. Android自定义控制(五)仿新浪微博的下拉刷新

    网上有很多很有名的开源框架,这里就来拉拉PullToRefresh这个框架,也就是我们平时用的下拉刷新啦,当然你问我这个有什么用啊?别人已经写好了,这里主要是学习以及练习,练习的次数多了,一切就顺其自 ...

  6. Android开发笔记(一百六十四)仿京东首页的下拉刷新

    上一篇文章介绍了高仿京东的沉浸式状态栏,可是跟京东首页的头部轮播图相比,依然有三处缺憾: 1.京东的头部Banner上方,除了有悬浮着的状态栏,状态栏下面还有一行悬浮工具栏,内嵌扫一扫图标.搜索框,以 ...

  7. LIstView多条目展示+XListView下拉刷新,上拉加载

    简单的一个listView展示网络上的资源文件,并使用XListView第三方控件.下面进行简单的代码展示. 使用XListView要先把源码导入,包括XListView,XListViewFoote ...

  8. 微信小程序自定义导航栏与自带下拉刷新冲突

    在使用HbuilderX开发微信小程序时(在HbuilderX中写代码,在微信开发者工具编译),由于微信小程序自带的导航栏功能少,样式单一等,于是采用colorUI自定义导航栏实现需求.而问题就一而再 ...

  9. 【Flutter】ListView 列表高级功能 ( RefreshIndicator 下拉刷新组件 )

    文章目录 一.下拉刷新组件 二.下拉刷新代码示例 三.相关资源 一.下拉刷新组件 使用 Flutter 提供的 RefreshIndicator 组件 , 可以实现下拉刷新的功能 ; 使用 Refre ...

  10. Android项目实战(十六):QQ空间实现(一)—— 展示说说中的评论内容并有相应点击事件...

    大家都玩QQ空间客户端,对于每一个说说,我们都可以评论,那么,对于某一条评论: 白雪公主 回复 小矮人 : 你们好啊~ 我们来分析一下: 1.QQ空间允许我们 点击 回复人和被回复人的名字就可以进入对 ...

最新文章

  1. Python:C语言扩展
  2. linux国内计算机系统,计算机系统进化论 | Linux 中国
  3. iOS 开发之--使用AFNetWorking3.1.0上传单张/多张图片
  4. 纯java应用搭建,16、BoneCp纯java项目使用
  5. html与js与mysql_WebView加载html与JS交互
  6. SPOJ1716 GSS3(线段树)
  7. 【英语学习】【WOTD】thole 释义/词源/示例
  8. 锐角三角函数用计算机怎么算,用计算器求锐角三角函数值和由锐角三角函数值求锐角.doc...
  9. linux安装gcc9.1
  10. Mac上redis下载安装与配置详细版
  11. weblogic安装失败常见问题
  12. 2019年中科院信工所复试经验帖
  13. java简单选择排序以及时间复杂度
  14. 怎么设置计算机键盘数字键,电脑右边的数字键不能用怎么办_电脑右边数字键盘用不了的修复方法...
  15. php 表格内边距,CSS 内边距
  16. ubuntu22.04美化、办公、开发工具安装
  17. git下载,上传代码到GitLab ; Untracked files (use “git add <file>...“ to include in what will be committed)
  18. cmd中cd命令使用
  19. 常见交通工具英语单词
  20. java 12306验证码识别_GitHub - sunqipeng-cn/JavaVerify: 用java 编写的验证码识别

热门文章

  1. html与css笔记(旧)
  2. web端如何获取笔压 web端获取笔压的js库
  3. win10触屏输入法_触摸屏笔记本Win10电脑的校准技巧
  4. 物联网的媒介——java usb串口通信
  5. 解空间树及其相关算法
  6. 姓潘取名:潘姓有气质的女孩名字
  7. 基于VEH调试寄存器实现无痕HOOK(5)
  8. 万字长文!多图预警!46张图彻底搞懂 IP 基础知识!
  9. CVE-2020-7961 Liferay Portal 命令执行漏洞
  10. android 录音机添加书签,带录音的书签的制作方法