新聞中心
1、Android:clipToPadding

鎮(zhèn)雄ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書合作)期待與您的合作!
意思是控件的繪制區(qū)域是否在padding里面。默認(rèn)為true。如果你設(shè)置了此屬性值為false,就能實(shí)現(xiàn)一個(gè)在布局上事半功陪的效果。先看一個(gè)效果圖。
上圖中的ListView頂部默認(rèn)有一個(gè)間距,向上滑動(dòng)后,間距消失,如下圖所示。
如果使用margin或padding,都不能實(shí)現(xiàn)這個(gè)效果。加一個(gè)headerView又顯得大材小用,而且過(guò)于麻煩。此處的clipToPadding配合paddingTop效果就剛剛好。
同樣,還有另外一個(gè)屬性也很神奇:android:clipChildren,具體請(qǐng)參考:【Android】神奇的android:clipChildren屬性(http://www.cnblogs.com/over140/p/3508335.html)
2、match_parent和wrap_content
按理說(shuō)這兩個(gè)屬性一目了然,一個(gè)是填充布局空間適應(yīng)父控件,一個(gè)是適應(yīng)自身內(nèi)容大小。但如果在列表如ListView中,用錯(cuò)了問(wèn)題就大了。ListView中的getView方法需要計(jì)算列表?xiàng)l目,那就必然需要確定ListView的高度,onMesure才能做測(cè)量。如果指定了wrap_content,就等于告訴系統(tǒng),如果我有一萬(wàn)個(gè)條目,你都幫我計(jì)算顯示出來(lái),然后系統(tǒng)按照你的要求就new了一萬(wàn)個(gè)對(duì)象出來(lái)。那你不悲劇了?先看一個(gè)圖。
假設(shè)現(xiàn)在ListView有8條數(shù)據(jù),match_parent需要new出7個(gè)對(duì)象,而wrap_content則需要8個(gè)。這里涉及到View的重用,就不多探討了。所以這兩個(gè)屬性的設(shè)置將決定getView的調(diào)用次數(shù)。
由此再延伸出另外一個(gè)問(wèn)題:getView被多次調(diào)用。
什么叫多次調(diào)用?比如position=0它可能調(diào)用了幾次。看似很詭異吧。GridView和ListView都有可能出現(xiàn),說(shuō)不定這個(gè)禍?zhǔn)拙褪莣rap_content。說(shuō)到底是View的布局出現(xiàn)了問(wèn)題。如果嵌套的View過(guò)于復(fù)雜,解決方案可以是通過(guò)代碼測(cè)量列表所需要的高度,或者在getView中使用一個(gè)小技巧:parent.getChildCount == position
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (parent.getChildCount() == position) {
- // does things here
- }
- return convertView;
- }
3、IllegalArgumentException: pointerIndex out of range
出現(xiàn)這個(gè)Bug的場(chǎng)景還是很無(wú)語(yǔ)的。一開(kāi)始我用ViewPager + PhotoView(一個(gè)開(kāi)源控件)顯示圖片,在多點(diǎn)觸控放大縮小時(shí)就出現(xiàn)了這個(gè)問(wèn)題。一開(kāi)始我懷疑是PhotoView的bug,找了半天無(wú)果。要命的是不知如何try,老是crash。后來(lái)才知道是android遺留下來(lái)的bug,源碼里沒(méi)對(duì)pointer index做檢查。改源碼重新編譯不太可能吧。明知有exception,又不能從根本上解決,如果不讓它c(diǎn)rash,那就只能try-catch了。解決辦法是:自定義一個(gè)ViewPager并繼承ViewPager。請(qǐng)看以下代碼:
- /**
- * 自定義封裝android.support.v4.view.ViewPager,重寫onInterceptTouchEvent事件,捕獲系統(tǒng)級(jí)別異常
- */
- public class CustomViewPager extends ViewPager {
- public CustomViewPager(Context context) {
- this(context, null);
- }
- public CustomViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- try {
- return super.onInterceptTouchEvent(ev);
- } catch (IllegalArgumentException e) {
- LogUtil.e(e);
- } catch (ArrayIndexOutOfBoundsException e) {
- LogUtil.e(e);
- }
- return false;
- }
- }
把用到ViewPager的布局文件,替換成CustomViewPager就OK了。
4、ListView中item點(diǎn)擊事件無(wú)響應(yīng)
listView的Item點(diǎn)擊事件突然無(wú)響應(yīng),問(wèn)題一般是在listView中加入了button、checkbox等控件后出現(xiàn)的。這個(gè)問(wèn)題是聚焦沖突造成的。在android里面,點(diǎn)擊屏幕之后,點(diǎn)擊事件會(huì)根據(jù)你的布局來(lái)進(jìn)行分配的,當(dāng)你的listView里面增加了button之后,點(diǎn)擊事件***優(yōu)先分配給你listView里面的button。所以你的點(diǎn)擊Item就失效了,這個(gè)時(shí)候你就要根據(jù)你的需求,是給你的item的最外層layout設(shè)置點(diǎn)擊事件,還是給你的某個(gè)布局元素添加點(diǎn)擊事件了。
解決辦法:在ListView的根控件中設(shè)置(若根控件是LinearLayout, 則在LinearLayout中加入以下屬性設(shè)置)descendantFocusability屬性。
- android:descendantFocusability="blocksDescendants"
官方文檔也是這樣說(shuō)明。
5、getSupportFragmentManager()和getChildFragmentManager()
有一個(gè)需求,F(xiàn)ragment需要嵌套3個(gè)Fragment。基本上可以想到用ViewPager實(shí)現(xiàn)。開(kāi)始代碼是這樣寫的:
- mViewPager.setAdapter(new CustomizeFragmentPagerAdapter(getActivity().getSupportFragmentManager(), subFragmentList));
導(dǎo)致的問(wèn)題是嵌套的Fragment有時(shí)會(huì)莫名其妙不顯示。開(kāi)始根本不知道問(wèn)題出現(xiàn)在哪,當(dāng)你不知道問(wèn)題的原因時(shí),去解決這個(gè)問(wèn)題顯然比較麻煩。經(jīng)過(guò)一次又一次的尋尋覓覓,終于在stackoverflow上看到了同樣的提問(wèn)。說(shuō)是用getChildFragmentManager()就可以了。真是這么神奇!
- mViewPager.setAdapter(new CustomizeFragmentPagerAdapter(getChildFragmentManager, subFragmentList));
讓我們看一下這兩個(gè)有什么區(qū)別。首先是getSupportFragmentManager(或者getFragmentManager)的說(shuō)明:
- Return the FragmentManager for interacting with fragments associated with this fragment's activity.
然后是getChildFragmentManager:
- Return a private FragmentManager for placing and managing Fragments inside of this Fragment.
Basically, the difference is that Fragment’s now have their own internal FragmentManager that can handle Fragments. The child FragmentManager is the one that handles Fragments contained within only the Fragment that it was added to. The other FragmentManager is contained within the entire Activity.
已經(jīng)說(shuō)得比較明白了。
6、ScrollView嵌套ListView
這樣的設(shè)計(jì)是不是很奇怪?兩個(gè)同樣會(huì)滾動(dòng)的View居然放到了一起,而且還是嵌套的關(guān)系。曾經(jīng)有一個(gè)這樣的需求:界面一共有4個(gè)區(qū)域部分,分別是公司基本信息(logo、名稱、法人、地址)、公司簡(jiǎn)介、公司榮譽(yù)、公司口碑列表。每部分內(nèi)容都需要根據(jù)內(nèi)容自適應(yīng)高度,不能寫死。鄙人首先想到的也是外部用一個(gè)ScrollView包圍起來(lái)。然后把這4部分分別用4個(gè)自定義控件封裝起來(lái)?;拘畔⒑凸竞?jiǎn)介比較簡(jiǎn)單,榮譽(yù)需要用到RecyclerView和TextView的組合,RecyclerView(當(dāng)然,用GridView也可以,3列多行的顯示)存放榮譽(yù)圖片,TextView顯示榮譽(yù)名稱。***一部分口碑列表當(dāng)然是ListView了。這時(shí)候,問(wèn)題就出來(lái)了。需要解決ListView放到ScrollView中的滑動(dòng)問(wèn)題和RecyclerView的顯示問(wèn)題(如果RecyclerView的高度沒(méi)法計(jì)算,你是看不到內(nèi)容的)。
當(dāng)然,網(wǎng)上已經(jīng)有類似的提問(wèn)和解決方案了。
給一個(gè)網(wǎng)址:
四種方案解決ScrollView嵌套ListView問(wèn)題(http://bbs.anzhuo.cn/thread-982250-1-1.html)
ListView的情況還比較好解決,優(yōu)雅的做法無(wú)非寫一個(gè)類繼承ListView,然后重寫onMeasure方法。
- @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
- super.onMeasure(widthMeasureSpec, expandSpec);
- }
ListView可以重寫onMeasure解決,RecyclerView重寫這個(gè)方法是行不通的。
說(shuō)到底其實(shí)計(jì)算高度嘛。有兩種方式,一種是動(dòng)態(tài)計(jì)算RecycleView,然后設(shè)置setLayoutParams;另外一種跟ListView的解決方式類似,定義一個(gè)類繼承LinearLayoutManager或GridLayoutManager(注意:可不是繼承RecyclerView),重寫onMeasure方法(此方法比較麻煩,此處不表,下次寫一篇文章再作介紹)。
動(dòng)態(tài)計(jì)算高度如下:
- int heightPx = DensityUtil.dip2px(getActivity(), (imageHeight + imageRowHeight) * lines);
- MarginLayoutParams mParams = new MarginLayoutParams(LayoutParams.MATCH_PARENT, heightPx);
- mParams.setMargins(0, 0, 0, 0);
- LinearLayout.LayoutParams lParams = new LinearLayout.LayoutParams(mParams);
- honorImageRecyclerView.setLayoutParams(lParams);
思路是這樣的:服務(wù)端返回榮譽(yù)圖片后,由于是3列顯示的方式,只需要計(jì)算需要顯示幾行,然后給定行間距和圖片的高度,再設(shè)置setLayoutParams就行了。
- int lines = (int) Math.ceil(totalImages / 3d);
至此,這個(gè)奇怪的需求得到了解決。
可是在滑動(dòng)的時(shí)候,感覺(jué)出現(xiàn)卡頓的現(xiàn)象。聰明的你肯定想到是滑動(dòng)沖突了。應(yīng)該是ScrollView的滑動(dòng)干擾到了ListView的滑動(dòng)。怎么辦呢?能不能禁掉ScrollView的滑動(dòng)?
百度一下,你肯定能搜索到答案的。先上代碼:
- /**
- * @author Leo
- *
- * Created in 2015-9-12
- * 攔截ScrollView滑動(dòng)事件
- */
- public class CustomScrollView extends ScrollView {
- private int downY;
- private int touchSlop;
- public CustomScrollView(Context context) {
- this(context, null);
- }
- public CustomScrollView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- }
- @Override
- public boolean onInterceptTouchEvent(MotionEvent e) {
- int action = e.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- downY = (int) e.getRawY();
- break;
- case MotionEvent.ACTION_MOVE:
- int moveY = (int) e.getRawY();
- if (Math.abs(moveY - downY) > touchSlop) {
- return true;
- }
- }
- return super.onInterceptTouchEvent(e);
- }
- }
只要理解了getScaledTouchSlop()這個(gè)方法就好辦了。這個(gè)方法的注釋是:Distance in pixels a touch can wander before we think the user is scrolling。說(shuō)這是一個(gè)距離,表示滑動(dòng)的時(shí)候,手的移動(dòng)要大于這個(gè)距離才開(kāi)始移動(dòng)控件,如果小于此距離就不觸發(fā)移動(dòng)。
看似很***了。
但是還有另外一個(gè)問(wèn)題:我每次加載這個(gè)界面花的時(shí)間太長(zhǎng)了,每次由其它界面啟動(dòng)這個(gè)界面時(shí),都要卡上1~2秒,而且因手機(jī)性能時(shí)間不等。并不是由于網(wǎng)絡(luò)請(qǐng)求,取數(shù)據(jù)由子線程做,跟UI線程毫無(wú)關(guān)系。這樣的體驗(yàn)自己看了都很不爽。
幾天過(guò)去了,還是那樣。馬上要給老板演示了。這樣的體驗(yàn)要被罵十次呀。
難道跟ScrollView的嵌套有關(guān)?
好吧,那我重構(gòu)代碼。不用ScrollView了。直接用一個(gè)ListView,然后add一個(gè)headerView存放其它內(nèi)容。因?yàn)榭丶庋b得還算好,沒(méi)改多少布局就OK了,一運(yùn)行,流暢順滑,一切迎刃而解!
本來(lái)就是這么簡(jiǎn)單的問(wèn)題,為什么非得用ScrollView嵌套呢?
stackoverflow早就告訴你了,不要這樣嵌套!不要這樣嵌套!不要這樣嵌套!重要的事情說(shuō)三遍。
ListView inside ScrollView is not scrolling on Android
(http://stackoverflow.com/questions/6210895/listview-inside-scrollview-is-not-scrolling-on-android)
當(dāng)然,從android 5.0 Lollipop開(kāi)始提供了一種新的API支持嵌入滑動(dòng),此時(shí),讓像這樣的需求也能很好實(shí)現(xiàn)。
此處給一個(gè)網(wǎng)址,大家有興趣自行了解,此處不再討論。
Android NestedScrolling 實(shí)戰(zhàn)
(http://www.race604.com/android-nested-scrolling/)
7、EmojiconTextView的setText(null)
這是開(kāi)源表情庫(kù)com.rockerhieu.emojicon中的TextView加強(qiáng)版。相信很多人用到過(guò)這個(gè)開(kāi)源工具包。TextView用setText(null)完全沒(méi)問(wèn)題。但EmojiconTextView setText(null)后就悲劇了,直接crash,顯示的是null pointer。開(kāi)始我懷疑時(shí)這個(gè)view沒(méi)初始化,但并不是。那就調(diào)試一下唄。
- @Override
- public void setText(CharSequence text, BufferType type) {
- SpannableStringBuilder builder = new SpannableStringBuilder(text);
- EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize);
- super.setText(builder, type);
- }
EmojiconTextView中的setText看來(lái)沒(méi)什么問(wèn)題。點(diǎn)SpannableStringBuilder進(jìn)去看看,源碼原來(lái)是這樣的:
- /**
- * Create a new SpannableStringBuilder containing a copy of the
- * specified text, including its spans if any.
- */
- public SpannableStringBuilder(CharSequence text) {
- this(text, 0, text.length());
- }
好吧。問(wèn)題已經(jīng)找到了,text.length(),不空指針才怪。
- text = text == null ? "" : text;
- SpannableStringBuilder builder = new SpannableStringBuilder(text);
加一行判斷就行了。
8、cursor.close()
一般來(lái)說(shuō),database的開(kāi)和關(guān)不太會(huì)忘記,但游標(biāo)的使用可能并不會(huì)引起太多重視,尤其是游標(biāo)的隨意使用。比如用ContentResolver結(jié)合Cursor查詢SD卡中圖片,很容易寫出以下的代碼:
- Cursor cursor = contentResolver.query(uri, null, MediaStore.Images.Media.MIME_TYPE + "=? or "
- + MediaStore.Images.Media.MIME_TYPE + "=?", new String[] { "image/jpeg", "image/png" },
- MediaStore.Images.Media.DATE_MODIFIED);
- while (cursor.moveToNext()) {
- // TODO
- }
cursor都不做非空判斷,而且往往在關(guān)閉游標(biāo)的時(shí)候不注意有可能異常拋出。
以前在項(xiàng)目中,經(jīng)常出現(xiàn)由于游標(biāo)沒(méi)及時(shí)關(guān)閉或關(guān)閉出異常沒(méi)處理好導(dǎo)致其它的問(wèn)題產(chǎn)生,而且問(wèn)題看起來(lái)非常的詭異,不好解決。后來(lái),我把整個(gè)項(xiàng)目中有關(guān)游標(biāo)的使用重構(gòu)一遍,后來(lái)就再?zèng)]發(fā)生過(guò)類似的問(wèn)題。
原則很簡(jiǎn)單,所有Cursor的聲明為:
- Cursor cursor = null;
且放在try-catch外面;需要用到cursor,先做非空判斷。然后在方法的***用一個(gè)工具類處理游標(biāo)的關(guān)閉。
- /**
- * @author Leo
- *
- * Created in 2015-9-15
- */
- public class IOUtil {
- private IOUtil() {
- }
- public static void closeQuietly(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (Throwable e) {
- }
- }
- }
- public static void closeQuietly(Cursor cursor) {
- if (cursor != null) {
- try {
- cursor.close();
- } catch (Throwable e) {
- }
- }
- }
- }
我想,這樣就沒(méi)必要在每個(gè)地方都try-catch-finally了。
9、java.lang.String cannot be converted to JSONObject
解析服務(wù)端返回的JSON字符串時(shí),居然拋出了這個(gè)異常。調(diào)試沒(méi)發(fā)現(xiàn)任何問(wèn)題,看起來(lái)是正常的JSON格式。后來(lái)發(fā)現(xiàn)居然是JSON串多了BOM(Byte Order Mark)。服務(wù)端的代碼由PHP實(shí)現(xiàn),有時(shí)開(kāi)發(fā)為了修改方便,直接用windows記事本打開(kāi)保存,引入了人眼看不到的問(wèn)題。其實(shí)就是多了”ufeff”這個(gè)玩意,客戶端代碼過(guò)濾一下就行了。
- // in case: Value of type java.lang.String cannot be converted to JSONObject
- // Remove the BOM header
- if (jsonStr != null) {
- jsonStr = jsonStr.trim();
- if (jsonStr.startsWith("ufeff")) {
- jsonStr = jsonStr.substring(1);
- }
- }
10、Shape round rect too large to be rendered into a texture
圓形矩形太大?
一開(kāi)始我發(fā)現(xiàn)一個(gè)acitivity中的scrollView滑動(dòng)一頓一頓的,而實(shí)際上沒(méi)有嵌套任何的列表控件如ListView、GridView,包含的無(wú)非是一些TextView、ImagView等??戳讼翬clipse中l(wèi)og輸出,發(fā)現(xiàn)出現(xiàn)了這個(gè)warn級(jí)別的提示。難道是我在外層嵌套了這個(gè)圓形矩形?我在很多地方都用了呀,為何就這個(gè)界面出現(xiàn)問(wèn)題了?
后來(lái)才發(fā)現(xiàn),這個(gè)圓形矩形包含的內(nèi)容太多了,已經(jīng)超出了手機(jī)的高度,而且可以滑好幾頁(yè)。
StackOverFlow上有人說(shuō):The easiest solution is to get rid of the rounded corners. If you remove the rounded corners and use a simple rectangle, the hardware renderer will no longer create a single large texture for the background layer, and won’t run into the texture size limit any more.
也有建議:to draw onto the canvas.
具體鏈接:How Do Solve Shape round rect too large to be rendered into a texture
(http://stackoverflow.com/questions/14519025/how-do-solve-shape-round-rect-too-large-to-be-rendered-into-a-texture-in-android)
我試了下自定義控件LinearLayout,通過(guò)canvas進(jìn)行draw,沒(méi)能解決。去掉radius屬性確實(shí)可行,可我想保留怎么辦?
還有一個(gè)解決辦法,通過(guò)在androidManifest.xml中禁用硬件加速,為了控制粒度,我只在此activity中禁用此功能。
先想到這么多,以后再補(bǔ)充。
分享題目:Android開(kāi)發(fā)的那些坑和小技巧
轉(zhuǎn)載注明:http://www.fisionsoft.com.cn/article/dpggcij.html


咨詢
建站咨詢
