Android自定义控件——有弹性的ListView,ScrollView
上一次我们试验了有弹性的ScrollView。详情
这一次,我们来试验有弹性的ScrollView。
国际惯例,效果图:
主要代码:
- import android.content.Context;
- import android.graphics.Rect;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.animation.Animation;
- import android.view.animation.Animation.AnimationListener;
- import android.view.animation.TranslateAnimation;
- import android.widget.AbsListView;
- import android.widget.ListView;
- /**
- * ElasticScrollView有弹性的ListView
- */
- public class ElasticListView extends ListView {
- private float y;
- private Rect normal = new Rect();
- private boolean animationFinish = true;
- public ElasticListView(Context context) {
- super(context);
- init();
- }
- public ElasticListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
- protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- }
- boolean overScrolled = false;
- private void init() {
- setOnScrollListener(new OnScrollListener() {
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- }
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- overScrolled = false;
- }
- });
- }
- @Override
- protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
- overScrolled = true;
- }
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- commOnTouchEvent(ev);
- return super.onTouchEvent(ev);
- }
- public void commOnTouchEvent(MotionEvent ev) {
- if (animationFinish) {
- int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- y = ev.getY();
- break;
- case MotionEvent.ACTION_UP:
- y = 0;
- if (isNeedAnimation()) {
- animation();
- }
- break;
- case MotionEvent.ACTION_MOVE:
- final float preY = y == 0 ? ev.getY() : y;
- float nowY = ev.getY();
- int deltaY = (int) (preY - nowY);
- y = nowY;
- // 当滚动到最上或者最下时就不会再滚动,这时移动布局
- if (isNeedMove(deltaY)) {
- if (normal.isEmpty()) {
- // 保存正常的布局位置
- normal.set(getLeft(), getTop(), getRight(), getBottom());
- }
- // 移动布局
- layout(getLeft(), getTop() - deltaY / 2, getRight(), getBottom() - deltaY / 2);
- }
- break;
- default:
- break;
- }
- }
- }
- // 开启动画移动
- public void animation() {
- // 开启移动动画
- TranslateAnimation ta = new TranslateAnimation(0, 0, 0, normal.top - getTop());
- ta.setDuration(200);
- ta.setAnimationListener(new AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- animationFinish = false;
- }
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- @Override
- public void onAnimationEnd(Animation animation) {
- clearAnimation();
- // 设置回到正常的布局位置
- layout(normal.left, normal.top, normal.right, normal.bottom);
- normal.setEmpty();
- animationFinish = true;
- }
- });
- startAnimation(ta);
- }
- // 是否需要开启动画
- public boolean isNeedAnimation() {
- return !normal.isEmpty();
- }
- // 是否需要移动布局
- public boolean isNeedMove(float deltaY) {
- if (overScrolled && getChildCount() > 0) {
- if (getLastVisiblePosition() == getCount() - 1 && deltaY > 0) {
- return true;
- }
- if (getFirstVisiblePosition() == 0 && deltaY < 0) {
- return true;
- }
- }
- return false;
- }
- }
测试代码:
- public class MainActivity extends Activity {
- ElasticListView listView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- listView = (ElasticListView) findViewById(R.id.listview);
- String[] listValues = new String[20];
- for (int i=0;i<listValues.length;i++) {
- listValues[i] = "TextView" + i;
- }
- listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listValues));
- }
- }
- public class MainActivity extends Activity {
- ElasticListView listView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- listView = (ElasticListView) findViewById(R.id.listview);
- String[] listValues = new String[20];
- for (int i=0;i<listValues.length;i++) {
- listValues[i] = "TextView" + i;
- }
- listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listValues));
- }
- }
public class FlexibleScrollView extends ScrollView { private Context mContext;
private static int mMaxOverDistance = 50; public FlexibleScrollView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView();
} public FlexibleScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initView();
} public FlexibleScrollView(Context context) {
super(context);
this.mContext = context;
initView();
} private void initView() {
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
float density = metrics.density;
mMaxOverDistance = (int) (density * mMaxOverDistance);
} @Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, mMaxOverDistance,
isTouchEvent);
}
}
通过上面这个类也可以实现弹性效果
二、仿朋友圈背景图片下拉
public class ScrollDampView extends ScrollView {
/** 该属性具体参数 怎么控制 未解!!!! */
private static final int LEN = 0xc8;
/** 回弹时所用的时间 */
private static final int DURATION = 200;
/** 最大Y坐标 其值一般设定为Scroller对应控件的高度 */
private static final int MAX_DY = 200; private Scroller mScroller;
/** 阻尼系数 */
private static final float OFFSET_RADIO = 2.5f; private float startY;
private int imageViewH; private ImageView imageView;
private boolean scrollerType; public ScrollDampView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); } public ScrollDampView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
} public ScrollDampView(Context context) {
super(context); } public void setImageView(ImageView imageView) {
this.imageView = imageView;
} float curY; @Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
if (!mScroller.isFinished()) {
return super.onTouchEvent(event);
}
switch (action) {
case MotionEvent.ACTION_DOWN:// 变量赋初始值
imageViewH = imageView.getHeight();
startY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (imageView.isShown()) {
float deltaY = (event.getY() - startY ) / OFFSET_RADIO;
Log.i("syso", "deltaY: "+deltaY+" imageTop: "+imageView.getTop());
//往下拉
if (deltaY > 0 && deltaY <= imageView.getBottom() + LEN) {
android.view.ViewGroup.LayoutParams params = imageView.getLayoutParams();
params.height = (int) (imageViewH + deltaY);// 改变高度
imageView.setLayoutParams(params);
}
scrollerType = false;
}
break;
case MotionEvent.ACTION_UP:
scrollerType = true;
// 开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位
// ,即到达坐标为(startX+dx , startY+dy)处
mScroller.startScroll(imageView.getLeft(), imageView.getBottom(),
0 - imageView.getLeft(),
imageViewH - imageView.getBottom(), DURATION);
invalidate();
break;
} return super.dispatchTouchEvent(event);
} // //该mScroller针对于imageView的变化
// 被父视图调用,用于必要时候对其子视图的值(mScrollX和mScrollY)
// 进行更新。典型的情况如:父视图中某个子视图使用一个Scroller对象来实现滚动操作,会使得此方法被调用。
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();// ImageView的当前Y坐标
imageView.layout(0, 0, x + imageView.getWidth(), y);// 使imageView本身做相应变化
invalidate();
// 滑动还未完成时,手指抬起时,当前y坐标大于其实imageView的高度时
// 设定imageView的布局参数 作用:使除imageView之外的控件做相应变化
if (!mScroller.isFinished() && scrollerType && y > MAX_DY) {
android.view.ViewGroup.LayoutParams params = imageView
.getLayoutParams();
params.height = y;
imageView.setLayoutParams(params);
}
}
} }
布局文件:
<com.example.dampview.ScrollDampView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dampview"
android:layout_width="match_parent"
android:layout_height="match_parent" > <LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" > <!--此处必须设置imageview的scaleType为centerCrop,当然在代码中设置也可以-->
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="160dp"
android:scaleType="centerCrop"
android:src="@drawable/image" /> <ImageView
android:id="@+id/iv_photo"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="-32dp"
android:src="@drawable/ic_launcher"
/> </LinearLayout> </com.example.dampview.ScrollDampView>
Android自定义控件——有弹性的ListView,ScrollView的更多相关文章
- android 自定义控件之NetWorkImageView 处理listview等控件中的图片加载乱序问题
0.调用: BaseAdapter中设置方法 holder.iv.loadImage(url); adapter_xxx.xml 中 控件需要用 xxx.NetWorkImageView 1 NetW ...
- Android自定义控件(三)——有弹性的ListView
上一次我们试验了有弹性的ScrollView.详情 这一次,我们来试验有弹性的ScrollView. 国际惯例,效果图: 主要代码: import android.content.Context; i ...
- 【转】Android自定义控件(三)——有弹性的ListView
原文地址:http://blog.csdn.net/a105865708/article/details/17959459 上一次我们试验了有弹性的ScrollView.详情 这一次,我们来试验有弹性 ...
- Android自定义控件(四)——让每一个Activity UI都具有弹性
前面我们已经介绍了如何让你的ScrollView,ListView具有弹性, 今天,我们在前面的基础上,做一下适当的修改,让那些既不是ScrollView,也不是ListView的Activity页面 ...
- android 关于listview scrollview 底部 控件无法显示的两个解决方案
方案一 用LinearLayout实现,代码如下: <!-- 中奖纪录 by mhd --> <LinearLayout xmlns:android="http://sch ...
- Android自定义控件 开源组件SlidingMenu的项目集成
在实际项目开发中,定制一个菜单,能让用户得到更好的用户体验,诚然菜单的样式各种各样,但是有一种菜单——滑动菜单,是被众多应用广泛使用的.关于这种滑动菜单的实现,我在前面的博文中也介绍了如何自定义去实现 ...
- 【转】Android之自定义Adapter的ListView
http://www.cnblogs.com/topcoderliu/archive/2011/05/07/2039862.html 在开发中,我们经常使用到ListView这个控件.Android的 ...
- android 自定义控件二之仿QQ长按删除
自定义Dialog 1.先上个效果图:
- Android之自定义Adapter的ListView
ListView的创建,一般要具备两大元素: 1)数据集,即要映射的字符串.图片信息之类. 2)适配器,实现把要映射的字符串.图片信息映射成视图(如Textview.Image等组件),再添加到Lis ...
随机推荐
- 最小生成树(prim)
里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且 ...
- MySQL的约束、多表查询、子查询
一.约束之主键约束 约束:约束是添加在列上的,用来约束列的. 1.主键约束(唯一标识):非空.唯一.被引用 当表的某一列被指定为主键后,该类就不能为空,不能有重复值出现 创建表时指定主键的两种方式: ...
- Bash拾遗:变量
使用引号包裹变量 在<高级Bash脚本编程指南>中的4.1节中有这么个例子: hello="A B C D" echo $hello # A B C D echo &q ...
- BZOJ1970 [Ahoi2005] 矿藏编码
Description 依次对每份进行编码,得S1,S2,S3,S4.该矿藏区的编码S为2S1S2S3S4. 例如上图中,矿藏区的编码为:2021010210001. 小联希望你能根据给定的编码统计出 ...
- javascript实现数据结构: 树和森林
树的3种常用链表结构 1 双亲表示法(顺序存储结构) 优点:parent(tree, x)操作可以在常量时间内实现 缺点:求结点的孩子时需要遍历整个结构 用一组连续的存储空间来存储树的结点,同时在每个 ...
- Raspberry install wine
sudo apt install wine winecfg出现问题 树莓派3B是卡片电脑,内存为1GB,一般运行Linux.Linux两种主流的内存分配方法2G/2G和3G/1G,树莓派系统后期优化性 ...
- Angular1.x directive(指令里的)的compile,pre-link,post-link,link,transclude
The nitty-gritty of compile and link functions inside AngularJS directives The nitty-gritty of comp ...
- 通过注解实现Spring 声明式事务管理
小Alan接着上一篇Spring事务管理入门与进阶做一些补充,如果对Spring事务管理还不了解的可以看看上一篇文章. 实例 在我们开始之前,至少有两个数据库表是至关重要的,在事务的帮助下,我们可以实 ...
- spring cloud zuul 配置
参考:http://www.ityouknow.com/springcloud/2017/06/01/gateway-service-zuul.html spring boot版本:2.0.3.REL ...
- django从1.7升级到1.9后 提示:RemovedInDjango110Warning
Django项目,把django从1.7升级到1.9后,大量报错.需要做如下修改. 1,修改urls.py: 在django1.9里,urls的配置不再支持字符串型的路由.需要先import,然后直接 ...