这里总结的都是平时一些常用的功能,使用系统提供的方法能更快的实现想要的效果
1.圆形裁剪 outlineProvider
想要实现圆形裁剪特别简单,我自己写了一个拓展方法,
fun View.zhomeOutLineProvider(block: (View?,Outline?) -> Unit){
this.outlineProvider=object :ViewOutlineProvider(){
override fun getOutline(view: View?, outline: Outline?) {
block(view,outline)
}
}
// 使用设定的边距
this.clipToOutline=true
}
使用这个拓展方法的情况,指定outline 为圆形就可以了,宽度为view 的宽,高度为View 的高度
iv_zhome_out_line_provider.zhomeOutLineProvider { view, outline ->
outline?.setOval(0,0,view?.width?:0,view?.height?:0)
}
View 设置完 ViewOutlineProvider 后, 阴影的范围也被改变了,举个栗子
View.outlineProvider=object :ViewOutlineProvider(){}
把一个矩形的View 修改为 一个圆形的View ,但是 clipToOutline 设置的false
那么只是将阴影的绘制区域给改变了,
如果 clipToOutline =true 则是将View 裁剪,同时改变阴影的显示
2.扩充点击范围 从QMUI中学习到这个方法
在将QMUI 的代码下载下来,研究了一下他们的工具类,找到了这个方法,放出他们的源码 ,
/**
* 扩展点击区域的范围
*
* @param view 需要扩展的元素,此元素必需要有父级元素
* @param expendSize 需要扩展的尺寸(以sp为单位的)
*/
public static void expendTouchArea(final View view, final int expendSize) {
if (view != null) {
final View parentView = (View) view.getParent();
parentView.post(new Runnable() {
@Override
public void run() {
Rect rect = new Rect();
view.getHitRect(rect); //如果太早执行本函数,会获取rect失败,因为此时UI界面尚未开始绘制,无法获得正确的坐标
rect.left -= expendSize;
rect.top -= expendSize;
rect.right += expendSize;
rect.bottom += expendSize;
parentView.setTouchDelegate(new TouchDelegate(rect, view));
}
});
}
}
发现TouchDelegate 可以扩充点击事件,但是他并不能将这个事件拓展到他的父ViewGroup之外,至于这个方法为什么可以用,我又去看了一下View 的onTouchEvent 的,发现了里面的玄机
public boolean onTouchEvent(MotionEvent event) {
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
/// 这里判断的click事件
}
}
如果View 存在mTouchDelegate ,这个事件的响应会在onClick之前, 而且这个范围也是你自己指定的,一个非常nice的方法,受教了
3.click 优雅防止多次出发事件 从QMUI中学习到这个方法,
我将这个方法给写成了拓展方法,方便使用,这里贴一下我改后的
/**
* 设定时间内,只让第一个事件执行
*/
fun throttleClick(wait: Long = 200, block: ((View) -> Unit)): View.OnClickListener {
return View.OnClickListener { v ->
val current = SystemClock.uptimeMillis()
val lastClickTime = (v.getTag(R.id.zhome_view_click) as? Long) ?: 0
if (current - lastClickTime > wait) {
v.setTag(R.id.zhome_view_click, current)
block(v)
}
}
}
/**
* 简化上面的写法
*/
fun View.click(wait: Long = 200, block: ((View) -> Unit)){
this.setOnClickListener(throttleClick(wait,block))
}
他们写的这个非常巧妙,还有另外一个也是非常好用,
/**
* 设定时间内,让最后一个事件执行
*/
fun debounceClick(wait: Long = 200, block: ((View) -> Unit)): View.OnClickListener {
return View.OnClickListener { v ->
var action = (v.getTag(R.id.zhome_view_click) as? DebounceAction)
if(action == null){
action = DebounceAction(v, block)
v.setTag(R.id.zhome_view_click, action)
}else{
action.block = block
}
v.removeCallbacks(action)
v.postDelayed(action, wait)
}
}
class DebounceAction(val view: View, var block: ((View) -> Unit)): Runnable {
override fun run() {
if(view.isAttachedToWindow){
block(view)
}
}
}
虽然感觉自己写UI相关的东西挺多的,了解的东西也算不少了,但是看了他们的代码后,对自己的提升还是非常大的,
下面附上他们项目的链接 https://github.com/Tencent/QMUI_Android
4.clipdrawable 查看progressbar的时候发现一个level属性,查了相关文章后发现超级实用
前一阵子在开发的过程中遇到这样一个需求
这个seekbar 拖动的部分是一个渐变色,背景是不变的,最开始打算修改seekbar,或者从网上找一些资料,但是效果都不尽如人意,没办法翻了一下progressbar,想从他的源码中发现他是如何实现的这个效果,就找到了clipdrawable
具体一个具体的clip 文件
其中 clipOrientation 指定的是水平渲染还是垂直渲染 gravity 指定的是从哪里开始渲染
剩下的都是draw的属性了,最终实现的效果
5.RecyclerView 使用Scroller滚动到指定位置 用的地方很多,这个的是使用动画滚动过去,
RecyclerView 滚动的指定item,原生的api是只要这个item的在屏幕上就可以了,不管这个item是否在屏幕的一个上面,从网上看了很多例子,大部分都是根据layoutmanager 来判断的,实现起来超级复杂,下面来看一下我这套方案
class TopSmoothScroller(context: Context?) : LinearSmoothScroller(context) {
override fun getHorizontalSnapPreference(): Int {
return SNAP_TO_START //意思是吸附在顶部
}
override fun getVerticalSnapPreference(): Int {
return SNAP_TO_START //意思是吸附在顶部
}
}
我是重写了一个recyclerview ,把他的smoothScrollToPosition 给替换了,
@Override
public void smoothScrollToPosition(int position) {
if(getContext()!=null){
LinearSmoothScroller s1 =new TopSmoothScroller(getContext());
s1.setTargetPosition(position);
if(getLayoutManager()!=null){
getLayoutManager().startSmoothScroll(s1);
}
}
}
方便快捷
6.colorfliter 修改颜色,不用替换iamge 节省内存 ,titlebar 滑动渐变初使用
在titlebar 滑动由透明色背景变成白色背景,到达中间时,需要替换返回图片,和右边的按钮,如果使用图片的资源id 来回设置的话,如果图片加载比较多,会发现明显的卡顿,这是内存抖动造成的,如果使用这个方法
img.setColorFilter(new PorterDuffColorFilter(ContextCompat.getColor(child.getContext(), R.color.color_ct1_85), PorterDuff.Mode.SRC_IN));
想要变回来也特别简单
img.setColorFilter(null)
其实在做这个功能的时候,我的想法是能不能将这个滑动渐变的过程整体封装起来,只要使用了我写的titlebar,整个滑动渐变的过程对外都是没有感知的,要不然每次都监听ScrollView 只是copy代码都有点麻烦,我就自己写了一个
SimpleToolBarAlphaBehavior
public class SimpleToolBarAlphaBehavior extends CoordinatorLayout.Behavior {
private float deltaY;
// public int textColor;
private SimpleToolbarListeners listeners;
public boolean blackToolBar = false;
public SimpleToolBarAlphaBehavior(Context context) {
super();
}
public SimpleToolBarAlphaBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof RecyclerView || dependency instanceof NestedScrollView || dependency instanceof SmartRefreshLayout || dependency instanceof ViewPager2;
}
public void onConfigChange(){
deltaY=0;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (deltaY == 0) {
deltaY = dependency.getY();
}
float dy =dependency.getY();
float pre = prepareDate(dy);
Log.i("tian.shm","pre:"+pre);
int alpha = (int) ((pre) * 255);
int color=Color.argb(alpha,255,255,255);///白色
child.setBackgroundColor(color);
addListeners(alpha,pre,child);
return true;
}
public void setSimpleToolbarListeners(SimpleToolbarListeners listeners) {
this.listeners = listeners;
}
private void addListeners(int alpha,float pre,View child) {
if(listeners!=null){
listeners.onAlphaChange(pre);
}
if (alpha >= 127) {
if (!blackToolBar) {
blackToolBar = true;
if(child instanceof TitleBarView){
((TitleBarView) child).getLeftImageButton().setColorFilter(new PorterDuffColorFilter(ContextCompat.getColor(child.getContext(), R.color.color_ct1_85), PorterDuff.Mode.SRC_IN));
if(((TitleBarView) child).getRightImageButton()!=null){
((TitleBarView) child).getRightImageButton().setColorFilter(new PorterDuffColorFilter(ContextCompat.getColor(child.getContext(), R.color.color_ct1_85), PorterDuff.Mode.SRC_IN));
}
((TitleBarView) child).getCenterTextView().setTextColor(ContextCompat.getColor(child.getContext(), R.color.color_ct1_85));
}
if (listeners != null) {
listeners.onFullColor();
}
}
} else {
if (blackToolBar) {
blackToolBar = false;
((TitleBarView) child).getLeftImageButton().setColorFilter(null);
((TitleBarView) child).getCenterTextView().setTextColor(Color.WHITE);
if(((TitleBarView) child).getRightImageButton()!=null){
((TitleBarView) child).getRightImageButton().setColorFilter(null);
}
if (listeners != null) {
listeners.onTransparent();
}
}
}
}
private float prepareDate(float dy) {
if(dy==0f){
return 1;
}
dy = dy < 0 ? 0 : dy;
float pre = 1-(dy / deltaY);
if (pre > 0.33f) {
pre = 1;
} else {
pre = pre * 3;
}
return pre>1?1:pre;
}
public static SimpleToolBarAlphaBehavior from(V view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)) {
throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
} else {
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params).getBehavior();
if (!(behavior instanceof SimpleToolBarAlphaBehavior)) {
throw new IllegalArgumentException("The view is not associated with SimpleToolbarBehavior");
} else {
return (SimpleToolBarAlphaBehavior) behavior;
}
}
}
}
layout 文件的写法 只要CollapsingToolbarLayout 内部有高度,那么就会自己实现渐变改变颜色
这样就做到了整个滑动渐变对外是完全没有感知的
7.View 的显示和隐藏 GONE VISIBLE 与 alpha 比较
实现View 的显示和隐藏不止有 GONE VISIBLE ,修改alpha 有时也能达到相同的效果,我在使用ViewDragHelper 的过程中,如果拖动后改变View 的 visibility ,那么本次拖动就失效了,原因是 在拖动过程中使用的是类似ViewCompat.offsetTopAndBottom() 这种方法,这个方法虽然能实现非常丝滑的View 的平移,但是如果重新layout后,他就会重新回到原位,很不幸的是 修改visibility 后,由于宽高收到的了影响,ViewGroup要重新layout ,就导致失效了, 这时使用alpha就是一个非常好的选择,
8.clipChildren 裁剪Child
我相信大家的开发的过程中肯定会遇到ViewPager 居中,但是能看到左右两边的一部分,给需要留边的ViewGroup 添加android:clipChildren=true 这个属性就可以做到,
大致类似下面的效果,
其实类似这些技巧还有非常多,只不过不实际使用想不起来用哪些,自己想了一些就记录下来了,