Work Better Than Yesterday!
这里只记录一些常用的UI View的使用技巧,而非原理性东西。虽然网上一搜都有,但是也省得每次都找得这么麻烦,于是都记录在一个blog里面,找起来也方便,遇到过的坑记录也容易找到。
两者都是可以直接引入一个layout,达到布局重用。区别是,前者是随着引用以后跟着父布局即时渲染,这样效率不高;后者是你去调用viewstub.inflate()方法以后才会去渲染。
如果textview的width和height没有match_parent的话,那么设置centerVertical和centerHorizontal就会无效。
之前排版的问题解决不了,不如就设置一行,然后滚动。
上面的配置使得textview只有在获得焦点以后才会滚动,如需要一直滚动,需要重写三个方法。
一种方式是,字符是固定的,则然后可以再strings.xml里面设置:
另外一种方式是,字符是动态变化的,则然后可以再代码里面设置:
在strings.xml里面设置
然后布局里面不用改什么,网上说加一个属性autoLink="web"
,但是我加了还是不能跳转,然后网上又有说在代码里面加这一段
textView.setMovementMethod(LinkMovementMethod.getInstance());
我发现加了更加没有,连超链接的颜色都失效了。后来发现,上面代码需要,但是autoLink属性去掉即可。
editInput.setFilters(new InputFilter[] {new InputFilter.LengthFilter(2048)});
setOnTouchListener()比setOnClickListener()更快获得点击事件,后者会先弹出键盘,然后有时候就失效了。
android:textCursorDrawable=”@null”//这样表示光标颜色和字体颜色一致
如果要修改颜色就这样先定义一个drawable的xml文件color_cursor.xml
ProgressBar是继承View的,而SeekBar是继承ProgressBar实现的,实际上实现更加复杂。
关于以前和康佳做的健身软件的转圈百分比UI,其实是自己画的。可以直接去看fitness的代码。
如果直接在布局里面添加一个ProgressBar的标签,那么默认是转圈圈的那个进度条,其实在AS的图形界面添加UI空间的时候可以直接拉过去的,发现也是有横向的ProgressBar的。 添加过后发现多了一个style:
style=”?android:attr/progressBarStyleHorizontal”
同样,转圈圈的style是:
style=”?android:attr/progressBarStyle”
这个横向的也行:
style=”@android:style/Widget.ProgressBar.Horizontal”
这个是在YY做短拍的时候编辑视频实现的,已经开源在GitHub了;然后在UC的时候做下载拿了MultiRangeBar来使用,实现了下载进度条的随机切分分段下载的效果。
在公司的项目里面做一个下载进度条显示的功能,因为进度条有不同的状态,所以通过setProgressDrawable来实现,因此代码的实现在每次的回调,除了设置setProgress外,还调用了setProgressDrawable。这就出现了一个问题,第二次设置progressdrawable的时候就显示不了进度条了。解决方法就是,在这两个set之前,先获取bounds:
Rect bounds = progressbar.getProgressDrawable().getBounds();
在这两个set之后:
progressbar.getProgressDrawable().setBounds(bounds); progressbar.setProgress(progressbar.getProgress() + 1); progressbar.setProgress(progressbar.getProgress() - 1);
具体不知道为什么这样就能实现,猜测是触发了重新绘制。
做公司的项目的时候,UI设计的一个进度条叫流光进度条,效果就是windows复制文件的是时候那个进度条,一个绿色的光从头跑过去。本来实现起来也挺麻烦的,因为设计到一些绘制的知识根本还不了解,还好,从GitHub上找到了一个开源的实现。引入开源的东西总是有不少问题的,然后就是自己修复使用了,具体去看公司项目代码即可。
progressbar左右的padding好像默认不是0,所以要设置一下
就是一个切换开关的控件,这个控件是4.0以后才有的,如果想要低版本用,可以直接拿官网的源码来用。这也说明了一点,其实如果有时候一些新版本的控件也可以这样拿来用。在YY做客户端的时候,那时候最低支持是2.3,又要做一个开关控件,而且样式是iOS那样的,于是,我也忘记了当时是从那里找来的,其实源码就是官方的源码,好像是某人抽离了出来,到底是当时YY的同事做的,还是网上找到,已经不重要了。如果需要用的话,可以直接找回来以前YY的代码。
现在UC做的一个项目,也用到了这个控件,但是样式也有点不同了,于是还是从YY那边搞来了代码,但是有不少问题,于是我就修改了一下源码,整合成只有一个类,应该是比之前还要好用一些的,所以如果需要可以从这里找到代码使用。
关于使用这段代码的时候,会有很多自定义属性的情况,还有style,attrs,drawable等配置,关于这些的原理可以看学习View的Blog,这里就不列举Switch的使用了,直接拷代码就可以了。
发现如果listview的每一项item没有图片的话,不管怎么设置item的布局高度都是无效的,网上查过以后,发现在item的布局里面设置最小高度就可以了。
如果要定制按下的颜色变化,就需要自己写一个selector,这里会有点问题,如果每个item本来就有背景颜色的,然后selector只是写成了这样:
这个selector对于正常的点击是好用的,但是因为listview的item有很多状态:
所以如果还是用上面的selector的话就会有问题,默认的颜色和item的背景重叠了。修改如下问题就解决了:
listview作为聊天界面的时候,要自动滚到底部,可以设置一个定时器,然后执行下面代码就可以了。
chatList.setSelection(fragmentAdapter.getCount() - 1);
setSelection是直接跳过去,而smoothScrollToPosition是滚过去。
遇到一个奇怪问题,在聊天界面,聊天内容如果用的是listview的话,点击输入框弹出键盘以后,listview会自动滚到底部,但是公司的是用PullToRefreshView,虽然是继承listview的,但是就不会滚动到底部,试过用上面的定时器方法,但是时间不好控制。也试过重写Layout来捕获OnResize方法,但是竟然还是无效。后来在stackoverflow上面找到了,加入这个属性就行:
android:transcriptMode=”alwaysScroll”
listview默认应该是normal,估计PullToRefreshView做了手脚。
android:dividerHeight=”10dp”
给listview设置一个黏住,浮起来不动的标题:StickyListHeaders
一般调用notifyDataSetChanged()就会触发getView()方法。然后我在项目这里发现了不管怎么调用notifyDataSetChanged都不会触发getView,想了一天都不能解决,后来才发现,必须在UI线程调用啊。
viewpager可以实现屏幕滑动的效果,用来做画廊,启动界面这些都不错,而且比Flipper,Gallery都快,顺畅。可以考虑把tongli的那个图片换成viewpager。
首先在布局设置viewpager控件,然后重写PagerAdapter,里面的data是List
viewpager可以设置setOnPageChangeListener事件。
一般结合viewpager使用的时候在顶部有一些tab,当滑动viewpager的时候,tab也跟着变化。官方的有这两个控件,他们都是作为viewpager的子控件使用:
PagerTabStrip: 交互式
PagerTitleStrip: 非交互式
PagerTabStrip:
1.点击上面的标题可以实现ViewPager的切换。
2.选中的文字下方包含指引线
3.显示全宽下划线(setDrawFullUnderline)
PagerTitleStrip:
1.点击上面的标题无反应。
2.无上述描述。
代码上没多少区别,viewpager和strip无需再绑定了,只要在上面的adapter里面再重写一个方法和设置title的data
另外,strip有很多属性可以设置的。
有一个开源的strip,那些tap是可以滑动的:pagerslidingtabstrip,如果要用它的时候再认真去学,公司就是用这个的,然后再适当修改为自己需求的。
比较成熟和出名的是这个Android-ViewPagerIndicator
viewpager的adapter的另外一种写法,不是继承PagerAdapter,而是继承FragmentPagerAdapter,没什么难的,只是viewpager的每一个view都是fragment而已。而且,这个adapter还需要传FragmentManager进去。
FragmentPagerAdapter更多的用于少量界面的ViewPager,比如Tab。划过的fragment会保存在内存中,尽管已经划过。而FragmentStatePagerAdapter和ListView有点类似,会保存当前界面,以及下一个界面和上一个界面(如果有),最多保存3个,其他会被销毁掉。
可以google一下这个,说是修复被回收后重启时不能从fragmentstatepageradapter恢复的bug。但是我还是不太懂,先记录下来,也许以后遇到这个问题就懂了。
设置mPager.setOffscreenPageLimit(3)
viewpager的适配器会预装在3个view,包含当前显示的view,一共4个,这是一个窗口,移动窗口时候,后面加载一个,前面销毁一个,最好能重写adapter的onDestoryItem方法。如果fragment缓存了的话,就不会去执行getItem方法,就会不去new Fragment了。
由于公司项目有需求,viewpager加一个按钮,点击然后滑动到下一个页面,直接去调用mViewPager.setCurrentItem(mPos, true)
发现切换得很快。调查发现,可以用反射修改viewpager的scroller属性。
给viewpager设置onTouch事件,如果返回true则可以屏蔽viewpager手动滑动。
例子如下:
方法一:创建子view的时候设置一个ID,然后可以根据这个id来find。
方法二:在adapter里面设置。
方法一在viewpager的onpagechange事件里面使用很好,方法二就不行了,因为是会先处理onpagechange事件,再调用adapter里面的方法来设置。注意,mPager.childAt(mPager.getCurrentItem());这个方法不准确,viewpager超过三个就不行了。
在聊天的应用里面这个非常有用,输入框显示图片,聊天内容显示表情,图片,语音等等。
SpannableString实现了CharSequence,Spannable接口;SpannableStringBuilder实现CharSequence,Spannable,Editable接口;Editable继承CharSequence,Spannable接口;ImageSpan继承DynamicDrawableSpan。
EditText里面放的一般是SpannableStringBuilder。new一个ImageSpan,然后把图片或者表情传进去。然后拿到EditText里面的Editable对象后调用spannable.setSpan(what, start, end, flag);what传的就是ImageSpan对象,start, end就是替换edittext里面的字符串。
flag参数有下面这些,它是用来标识在 Span 范围内的文本前后输入新的字符时是否把它们也应用这个效果:
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前后都不包括)
Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,后面不包括)
Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,后面包括)
Spanned.SPAN_INCLUSIVE_INCLUSIVE(前后都包括)
除了ImageSpan外,还有StyleSpan,URLSpan等等很多。
如此的简单!~之前公司的项目,一个搜索功能,以前的人写的时候,在搜索结果页要把关键字高亮,然他就用String.replaceAll()方法来把关键字替换html的标签。然后没有处理到一些情况,如果用户输入正则表达式就会崩溃。因为这个方法就是用正则表达式来替换的。我开始用Matcher.quoteReplacement(searchKey),但是会抛OOM错,由于转移递归了。后面处理方法就是遍历字符串,用spannable高亮了。
Canvas是画布的意思,没有深入了解过这个类,不知道是不是一块内存,不过,可以理解为一块显示区域,因为,画布嘛,不就是给人看的么,内存又不是给人看的,所以,bitmap才是一块内存,包含像素,要把bitmap画到canvas上面去,当然还可以画很多元素。一般,在View里面的onDraw方法都带有一个canvas,可以用来画东西。调用invalidate()方法会触发onDraw()方法。
public void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint);
oval是圆所在的矩阵,startAngle是起始角度,sweepAngle是弧的角度,useCenter是否显示半径连线,即是否画到圆心,最后是paint画笔。
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint);
画bitmap,left,top都容易理解,就是距离左边和上边的距离。
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint);
画bitmap,src是要截取原本bitmap的哪些区域,传null进去就是画整个bitmap;dst是要画在屏幕的哪些区域,Rect(left, top, right, bottom),left和top是左上角距左边和上边的距离,right是右上角(右边那条边)距左部的距离,bottom是左下角(底边)距顶部的距离。
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint);
画圆角矩阵,rect就是这个矩阵的坐标,paint是画笔,rx是x方向上的圆的半径,ry是y方向上的圆的半径,比较难理解,取其中一只角,可以平均切成两端弧,一段是水平方向(x方向),一段是垂直方向(y方向),这两段弧分别是来自不同半径的圆,这样就理解了。
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint);
画文字,x和y分别是文字的起始位置,是相对左上角的,值得注意的是,文字是从baseline那里开始画起的,并不是左上角。
Paint是画笔的意思,其实画笔就是带有颜色(Color)和样式(Styles)这些属性。调用canvas画东西的时候,必须传入一个画笔。画笔还可以设置锯齿,argb,字体大小,边框等属性。
更多Paint的方法:
paint.setAntiAlias(true);//消除锯齿
paint.ascent();
paint.descent();//这些方法获取一定的距离值,如下图
1.基准点是baseline
2.ascent:是baseline之上至字符最高处的距离
3.descent:是baseline之下至字符最低处的距离
4.leading:是上一行字符的descent到下一行的ascent之间的距离,也就是相邻行间的空白距离
5.top:是指的是最高字符到baseline的值,即ascent的最大值
6.bottom:是指最低字符到baseline的值,即descent的最大值
Color就是颜色类,里面定义不少常用颜色的常量值,也有不少好用的方法,例如分别可以提取argb的值。
ColorMatrix是颜色矩阵,非常厉害的类,理解可能比较困难,可以参考官网。可以通过矩阵相乘来改变颜色值,这个矩阵是4x5的,乘以颜色向量[R,G,B,A],矩阵乘法还记得吧,所以修改矩阵就可以修改argb了。可以参考我的RBPlayer里面的colorview类。这个的类的应用一般用在图片美化方面,改变图片的样式,泛黄啊,变灰啊什么的。
Matrix是修改位置,例如进行旋转,平移什么的,也非常强大,目前还没用到过,但是有一些比较厉害的应用就是实现Folding Layout,可以去Google一下。
对于自定义绘制圆形,或者圆角图片,可以使用Xfermode,Shader。