前言:本人菜鸟一枚,自定义控件经验不足,文章写得有不足或者错误的地方,恳请读者你指出!
一直到寻找弹性的View,也找到了很多,有满意的,有不满意的,但是有一点就是,他们写的我只会用,原理完全不懂。
像我这么“爱学习”的,要是不知道原理,心里总觉得缺少什么,于是我就在寻求那种简单明了的,原理清晰的文章,希望自己
看后也能写一篇属于自己的文章。于是才有了这篇简单的自定义弹性WebView。在这里,感谢这篇文章给我的基础与灵感。
WebView不像ScrollView那样里面有一个childView,所以自定义弹性的WebView会比自定义弹性的ScrollView简单得多。
原理:(这里只能说说我自己对这个弹性Webview的理解。不具有专业性)
一.需要有弹性效果的情景
1.当前的webview滑动到顶部,这时如果手指在屏幕上且往下Move,这时就需要webview整个布局向下滑动,手指抬起,布局弹回原来的位置,如果不能体会的,可以去用用苹果的体验一把。
2.当前的webview滑动到底部,这时如果手指在屏幕上且往上Move,这是就需要webview整个布局向上滑动,手指抬起,布局弹回原来的文职。
二.我认为的关键点:
1.如何判断webview在顶部。这个比较简单,直接通过getScrollY()==0来判断,如果为true,说明目前webView在顶部。
代码如下:
/** * 判断是否到达顶部,可以下拉 * * @return */ private boolean isCanPullDown() { return getScrollY() == 0; }
2.如何判断webView在底部呢,就是通过比较webview内容的高度与webview自身的高度+webview滑动的距离是否相等
来判断是否到达底部。这里需要注意的是由于webView可以通过手指来放大网页,所以在获取内容的高度的时候还需要注
意是获取当前缩放比例下的内容的高度。代码如下:
/** * 判断是不死到达底部,可以上拉 * * @return */ private boolean isCanPullUp() {
//小于的时候是内容的高度小于webview的高度的时候的情况
return ((
int) (getContentHeight() * getScale())) <= (getHeight() + getScrollY());
}
三.贴源码吧,如果因为好多需要明白的我都写在注释里了,这里在bb就等于让你看两遍,浪费时间。
public class FlexiableWebView extends WebView { //移动因子,比如在你可以上拉或者下拉的时候,手指在屏幕上移动了200px, // 那么布局只移动50px,这个可以根据需求自己调整,越大,越容易拉动 private static final float DISTANCE_SCALE = 0.25f; //动画执行的时间 private static final long ANIM_TIME = 300; //是否能够下拉 private boolean canPullDown = false; //是否能够上拉 private boolean canPullUp = false; //布局是否移动过 private boolean isMoved = false; //用来记录原始的布局位置信息,等下拉后者上拉后好还原成原来的位置 private Rect rect = new Rect(); //手指按下时的位置Y,如果手指滑动并没有移动,而是内容的滚动,则这个值实时更新为手指当前的位置Y private float startY = 0; //是不是第一次,该参数用于onlayout中,因为我发现WebView是ScrollView不一样, //WebView的onLayout方法会被连续调用,而ScrollView只调用一次 private boolean isOnce = false; public FlexiableWebView(Context context) { this(context, null); } public FlexiableWebView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlexiableWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); //指获取自一次的位置信息,后不在更改,这个地方在WebView中会执行多次, // 所以用一个boolean值来控制 if (!isOnce) { rect.set(this.getLeft(), this.getTop(), this.getRight(), this.getBottom()); isOnce = true; } } @Override public boolean onTouchEvent(MotionEvent e) { int action = e.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //手指按下时初始化参数 canPullDown = isCanPullDown(); canPullUp = isCanPullUp(); startY = e.getY(); break; case MotionEvent.ACTION_MOVE: if (!canPullUp && !canPullDown) { //跟定义参数的地方一样,如果不是有布局的下拉或者下拉引起的移动, // 这参数实时更新为的当前手指所在的位置 startY = e.getY(); canPullDown = isCanPullDown(); canPullUp = isCanPullUp(); //获取参数后并调试判断 break; } //获取此刻手指的Y值,用于计算滑动的距离 float nowY = e.getY(); //手指在屏幕上滑动的距离 int deltaY = (int) (nowY - startY); //判断是否应该移动布局 //1.webView滑动到顶部且手指下拉 //2.webView滑动到底部且手指上拉 //3.webView的内容小于webview的高度 boolean shouldMove = ((canPullDown && deltaY > 0) || (canPullUp && deltaY < 0) || (canPullUp && canPullDown)); if (shouldMove) { //利用滑动因子设置滑动的距离 int offset = (int) (deltaY * DISTANCE_SCALE); //更新布局的位置 this.layout(rect.left, rect.top + offset, rect.right, rect.bottom + offset); //布局已经更改 isMoved = true; } break; case MotionEvent.ACTION_UP: //如果布局没有更改,则跳出判断 if (!isMoved) break; // 开启动画 TranslateAnimation anim = new TranslateAnimation(0, 0, this.getTop(), rect.top); // 设置动画时间 anim.setDuration(ANIM_TIME); // 给view设置动画 this.setAnimation(anim); // 设置回到正常的布局位置 this.layout(rect.left, rect.top, rect.right, rect.bottom); // 将标志位重置 canPullDown = false; canPullUp = false; isMoved = false; break; } return super.onTouchEvent(e); } /** * 判断是否到达顶部,可以下拉 * * @return */ private boolean isCanPullDown() { return getScrollY() == 0; } /** * 判断是不死到达底部,可以上拉 * * @return */ private boolean isCanPullUp() { return ((int) (getContentHeight() * getScale())) <= (getHeight() + getScrollY()); } }四.使用。使用就跟原生的Webview一样的用法
布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ljy.lambdademo.MainActivity"> <com.ljy.lambdademo.FlexiableWebView android:id="@+id/wv" android:layout_width="match_parent" android:layout_height="match_parent"/>
</RelativeLayout>
MianActivity中的代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView wv = (WebView) findViewById(R.id.wv); WebSettings settings = wv.getSettings(); settings.setJavaScriptEnabled(true); wv.setWebViewClient(new WebViewClient()); wv.loadUrl("http://blog.163.com/hero_213/blog/static/3989121420115393913734/"); }
------请注意添加网络权限。
------有一个不足的地方就是在有些时候没有上拉时没有效果,如果哪位大神解决了请告诉小弟一声。
好了,就是这样了,如果有兴趣的可以去自己新建一个项目运行了看一看效果。^_^