背景:项目中有一个需求说要做自定义滚动条,而且要求滚动条在webview的左边,一看UI设计稿就蒙了,于是网上找了一堆结果发现没几个有用的要么就是要用积分换,泱泱大国内卷至此无语的很,还去Android bus上找了下,结果发现大背景下啥都不行。于是一怒之下自己写了一个!!!
废话不多说直接上效果图
原理:继承View自己写了一个控件
1. 传统滚动条有的功能他都有,可进行手势滑动,可与webView进行滑动联动
2. 可以控制滚动条在任意位置,和任意高度,不用与webview高度保持一致
3.可以控制滚动条宽度提高可触摸性,甩传统滚动条不好触摸滚动缺点一条街
4.暴露了接口可以进行到顶部到底部的相关逻辑处理
5.可以实现翻页功能,甩传统滚动条一条街
1. 滑动面太小,导致触摸范围太小
2.头部判断、底部判断不准确
3.指示器滑动范围不限制导致的一系列问题
4.webview的页面大小获取有误导致的问题
5.onDraw绘制逻辑,如果让轨迹和指示器只绘制居中等
package com.sample;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebView;
public class CustomScrollBar extends View {
private final static String TAG = "CustomScrollBar";
private Paint paint;
private float scrollPosition;
private WebView webView;
private float trackWidth = 5; // 轨迹的宽度
private int barWidth = 16;
private int barHeight;
private Context mContext;
public CustomScrollBar(Context context) {
super(context);
this.mContext = context;
init();
}
public CustomScrollBar(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
init();
}
public CustomScrollBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStyle(Paint.Style.FILL);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
barHeight = height / 3;
float left = (width - barWidth)/2;
float top = scrollPosition * (height - barHeight);
float right = width / 2 ;
float bottom = top + barHeight;
Log.e(TAG, "left = " + left + "top = " + top + " right = " + right + " bottom = " + bottom);
// 绘制轨迹
paint.setColor( Color.parseColor("#2B2E31"));
paint.setStrokeWidth(trackWidth);
float trackStartX = left + barWidth/4;
float trackEndX = left + barWidth /4;
float trackStartY = 0;
float trackEndY = height;
// 绘制轨迹
canvas.drawLine(trackStartX, trackStartY, trackEndX, trackEndY, paint);
// 绘制bar
paint.setColor(Color.parseColor("#8B8B8B"));
paint.setStrokeWidth(barWidth);
top = Math.max(Math.min(top, height - barHeight), 0);
bottom = Math.max(Math.min(bottom, height), barHeight);
canvas.drawRect(left, top < 0 ? 0 : top, right, bottom, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
float y = event.getY() / getHeight();
scrollToPosition(y);
return true;
default:
return super.onTouchEvent(event);
}
}
private void scrollToPosition(float position) {
if (webView != null) {
int contentHeight = (int) (webView.getContentHeight() * webView.getScale());
int webViewHeight = webView.getHeight();
int scrollRange = contentHeight - webViewHeight;
if (scrollRange > 0) {
int scrollTo = (int) (position * scrollRange);
Log.i(TAG,"scrollToPosition position = " + position + " scrollTo = " + scrollTo);
webView.scrollTo(0, Math.min(Math.max(scrollTo, 0), scrollRange));
}
}
}
private void updateScrollBarPosition() {
if (webView != null) {
// 获取 WebView 的滚动信息
int contentHeight = (int) (webView.getContentHeight() * webView.getScale());
int webViewHeight = webView.getHeight();
int scrollRange = contentHeight - webViewHeight;
// 计算滚动条位置
if (scrollRange > 0) {
scrollPosition = (float) webView.getScrollY() / scrollRange;
Log.i(TAG,"updateScrollBarPosition scrollPosition = " + scrollPosition );
invalidate();
}
}
}
public void setWebView(WebView webView) {
this.webView = webView;
// 设置 WebView 的滚动监听
if (webView != null) {
webView.setOnScrollChangeListener(new OnScrollChangeListener() {
@Override
public void onScrollChange(View view, int i, int i1, int i2, int i3) {
// 更新滚动条位置
updateScrollBarPosition();
if (onWebViewScrollChangedCallback != null) {
if (webView.getScrollY() == 0) {
onWebViewScrollChangedCallback.onScrollStatus(true, false);
} else if ( (int) (webView.getContentHeight() * webView.getScale() - webView.getHeight()) - webView.getScrollY() <=1) {
onWebViewScrollChangedCallback.onScrollStatus(false, true);
} else {
onWebViewScrollChangedCallback.onScrollStatus(false, false);
}
}
}
});
}
}
OnWebViewScrollChangedCallback onWebViewScrollChangedCallback;
public void setOnWebViewScrollChangedCallback(OnWebViewScrollChangedCallback callback) {
this.onWebViewScrollChangedCallback = callback;
}
// Interface for WebView scroll callback
public interface OnWebViewScrollChangedCallback {
void onScrollStatus(boolean frist, boolean last);
}
}
package com.sample;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebView;
public class CustomScrollBarNew extends View {
private final static String TAG = "CustomScrollBar";
private Paint paint;
private float scrollPosition;
private WebView webView;
private float trackWidth = 2f; // 轨迹的宽度
private float barWidth = 6f; //指示器宽度
private float barHeight;
private int trackColor = Color.parseColor("#2B2E31");
private int barColor = Color.parseColor("#8B8B8B");
public CustomScrollBarNew(Context context) {
super(context);
init(context);
}
public CustomScrollBarNew(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs,context);
}
public CustomScrollBarNew(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs,context);
}
private void init(Context context) {
paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStyle(Paint.Style.FILL);
}
private void init(AttributeSet attrs,Context context){
// 获取自定义属性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomScrollBar);
trackColor = typedArray.getColor(R.styleable.CustomScrollBar_trackColor, Color.parseColor("#2B2E31"));
barColor = typedArray.getColor(R.styleable.CustomScrollBar_barColor, Color.parseColor("#8B8B8B"));
trackWidth = typedArray.getDimension(R.styleable.CustomScrollBar_trackWidth, 2f);
barWidth = typedArray.getDimension(R.styleable.CustomScrollBar_barWidth, 8f);
typedArray.recycle(); // 记得回收
init(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
barHeight = getHeight() / 4;
float left = (width - barWidth) / 2;
float top = scrollPosition * (height - barHeight);
float right = width / 2;
float bottom = top + barHeight;
// 绘制轨迹
paint.setColor(trackColor );
paint.setStrokeWidth(trackWidth);
float trackStartX = left + barWidth / 4;
float trackEndX = left + barWidth / 4;
float trackStartY = 0;
float trackEndY = height;
// 绘制轨迹
canvas.drawLine(trackStartX, trackStartY, trackEndX, trackEndY, paint);
Log.e(TAG, "left = " + left + "top =" + top + " right" + right + " bottom" + bottom);
// 绘制bar
paint.setColor(barColor);
paint.setStrokeWidth(barWidth);
top = Math.max(Math.min(top, height - barHeight), 0);
bottom = Math.max(Math.min(bottom, height), barHeight);
canvas.drawRect(left, top, right, bottom, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
float y = event.getY() / getHeight();
scrollToPosition(y);
return true;
default:
return super.onTouchEvent(event);
}
}
private void scrollToPosition(float position) {
if (webView != null) {
int contentHeight = (int) (webView.getContentHeight() * webView.getScale());
int webViewHeight = webView.getHeight();
int scrollRange = contentHeight - webViewHeight;
if (scrollRange > 0) {
int scrollTo = (int) (position * scrollRange);
Log.e(TAG, "scrollToPosition scrollTo : " + scrollTo);
webView.scrollTo(0, Math.min(Math.max(scrollTo, 0), scrollRange));
}
}
}
private void updateScrollBarPosition() {
if (webView != null) {
// 获取 WebView 的滚动信息
int contentHeight = (int) (webView.getContentHeight() * webView.getScale());
int webViewHeight = webView.getHeight();
int scrollRange = contentHeight - webViewHeight;
// 计算滚动条位置
if (scrollRange > 0) {
scrollPosition = (float) webView.getScrollY() / scrollRange;
Log.e(TAG, "updateScrollBarPosition scrollPosition : " + scrollPosition);
invalidate();
}
}
}
public void setWebView(WebView webView) {
this.webView = webView;
// 设置 WebView 的滚动监听
if (webView != null) {
webView.setOnScrollChangeListener(new OnScrollChangeListener() {
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
Log.i(TAG," scrollX = " + scrollX + " scrollY = " + scrollY +" oldScrollX = " + oldScrollX + " oldScrollY = " + oldScrollY);
Log.i(TAG," canScrollVertically = " +webView.canScrollVertically(1));
// 更新滚动条位置
// 更新滚动条位置
updateScrollBarPosition();
if (onWebViewScrollChangedCallback != null) {
if (!webView.canScrollVertically(-1)) {
Log.e(TAG, "webView. 头部");
onWebViewScrollChangedCallback.onScrollStatus(true, false);
} else if (!webView.canScrollVertically(1)) {
onWebViewScrollChangedCallback.onScrollStatus(false, true);
Log.e(TAG, "webView. 底部");
} else {
Log.e(TAG, "webView. 中间");
onWebViewScrollChangedCallback.onScrollStatus(false, false);
}
}
}
});
}
}
private OnWebViewScrollChangedCallback onWebViewScrollChangedCallback;
public void setOnWebViewScrollChangedCallback(OnWebViewScrollChangedCallback callback) {
this.onWebViewScrollChangedCallback = callback;
}
// Interface for WebView scroll callback
public interface OnWebViewScrollChangedCallback {
void onScrollStatus(boolean frist, boolean last);
}
}
//是否到底部下面为true时
!webView.canScrollVertically(1)//判断是否在头部
!webView.canScrollVertically(-1)
private void scrollToPosition(float position) { if (webView != null) { int contentHeight = (int) (webView.getContentHeight() * webView.getScale()); int webViewHeight = webView.getHeight(); int scrollRange = contentHeight - webViewHeight; if (scrollRange > 0) { int scrollTo = (int) (position * scrollRange); Log.e(TAG, "scrollToPosition scrollTo : " + scrollTo); webView.scrollTo(0, Math.min(Math.max(scrollTo, 0), scrollRange)); } } }
(int) (webView.getContentHeight() * webView.getScale());