此排序沒有任何優先級或者重要程度。
此筆記只為記錄平時開發中碰到的經常用到確不太注意的一些問題,每次用過就忘記,還要重新搜索解決方案,所以在此積累下平時開發中碰到的一些常用而又容易忘記的簡單小bug。
-
Android 如何讓EditText不自動獲取焦點。
場景:有時候我們需要界面初始化時候并不希望EditText自動獲取焦點,因為自動獲取焦點會彈出軟鍵盤,此種場景并不是我們所需要的,所以我們要設置讓EditText不自動獲取焦點
解決方案:在EditText的父級控件中找一個,設置成如些:android:focusable="true" android:focusableInTouchMode="true"
這樣,就把EditText默認的行為攔截了。
或者采用下面這種方式:EditText對象的clearFocus(); InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(editMsgView.getWindowToken(), 0); //關閉軟鍵盤
-
Android 禁止初始時ScrollView自動滾動到底部。
場景:用ScrollView,加載數據時如果數據超過一屏的高度,有時會出現ScrollView自動滾動到底部,可能不是我們所需要的,我們可能需要的是從頂部開始顯示。
解決方案:在ScrollView子標簽LinearLayout里面加上:android:focusable="true" android:focusableInTouchMode="true"
如果出現某個控件搶占焦點造成的,可以禁止此控件的焦點。
-
Android 軟鍵盤彈出時把原來布局頂上去的解決方法。
場景:鍵盤彈出時,會將布局底部的導航條頂上去。
解決方案:在mainfest.xml中,在和導航欄相關的activity中添加如下代碼:<activity android:name=".MainActivity" android:windowSoftInputMode="adjustResize|stateHidden" />
擴展:
【A】stateUnspecified:軟鍵盤的狀態并沒有指定,系統將選擇一個合適的狀態或依賴于主題的設置 【B】stateUnchanged:當這個activity出現時,軟鍵盤將一直保持在上一個activity里的狀態,無論是隱藏還是顯示 【C】stateHidden:用戶選擇activity時,軟鍵盤總是被隱藏 【D】stateAlwaysHidden:當該Activity主窗口獲取焦點時,軟鍵盤也總是被隱藏的 【E】stateVisible:軟鍵盤通常是可見的 【F】stateAlwaysVisible:用戶選擇activity時,軟鍵盤總是顯示的狀態 【G】adjustUnspecified:默認設置,通常由系統自行決定是隱藏還是顯示 【H】adjustResize:該Activity總是調整屏幕的大小以便留出軟鍵盤的空間 【I】adjustPan:當前窗口的內容將自動移動以便當前焦點從不被鍵盤覆蓋和用戶能總是看到輸入內容的部分
-
在Android 6.0下繼續使用HttpClient。
HttpClient在Android 6.0已經被Google廢除了,如果想繼續使用HttpClient的話,只需在主Module的gradle中配置:android { useLibrary ‘org.apache.http.legacy‘ }
-
android 6.0以上運行時權限問題
- 報錯如下:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.liujc.supereader/com.liujc.supereader.ui.activity.ReadActivity}: java.lang.SecurityException: com.liujc.supereader was not granted this permission: android.permission.WRITE_SETTINGS.
-
解決方案:Android 中有兩個特殊權限,使用
requestPermission
方法是不成功的,需要特殊設置- SYSTEM_ALERT_WINDOW
- WRITE_SETTINGS
關于這兩個權限,需要我們自己手動開啟系統設置的Activity界面。后來打開系統設置頁面,才有這個權限的設置,設置下就ok了。
友好方式就是檢測到該權限獲取失敗,提示用戶跳轉到設置界面設置:- 設置彈框權限
//權限申請相關方法 private static final int REQUEST_CODE = 1; private void requestAlertWindowPermission() { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_CODE); } //回調 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE) { if (Settings.canDrawOverlays(this)) { Toast.makeText(this,"彈窗權限開啟!",Toast.LENGTH_SHORT).show(); PrefUtils.setBoolean(MainActivity.this, "isAllowAlert", true); }else { PrefUtils.setBoolean(MainActivity.this, "isAllowAlert", false); } } }
- 設置權限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 判斷是否有WRITE_SETTINGS權限 if(!Settings.System.canWrite(this)) { // 申請WRITE_SETTINGS權限 Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_CODE); } else { dosomething(); } } else { dosomething(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intentdata) { if (requestCode == REQUEST_CODE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 判斷是否有WRITE_SETTINGS權限 if (Settings.System.canWrite(this)) { dosomething(); } } } super.onActivityResult(requestCode, resultCode, data); }
-
在使用toolbar時報以下錯誤:
This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR
。
主要問題出在我們配置的style中:
錯誤寫法:<style name="AppTheme.NoActionBar"> <item name="android:windowActionBar">false</item> <item name="android:windowNoTitle">true</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> </style>
其中AppTheme使用的主題是AppCompat的主題,由于AppCompat主題下的windowActionBar和windowNoTitle的命名方式前都沒有android字樣,所以報錯。
正確寫法:<style name="AppTheme.NoActionBar"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> </style>
-
Android Studio導入項目報:錯誤: 非法字符: '\ufeff'。
出現場景:
復制的Eclipse項目的源文件粘貼到Android Studio上,在Android Studio上面編譯運行報錯:“錯誤: 非法字符: ‘\ufeff’”。
原因:
Eclipse可以自動把UTF-8+BOM文件轉為普通的UTF-8文件,但AndroidStudio需要重新轉一下 。
解決辦法:
將編碼格式UTF-8+BOM文件轉為普通的UTF-8文件。-
簡單方法,在AS右下角,將編碼改為GBK,再轉為UTF-8,可以解決。 這里寫圖片描述
- 用EditPlus
將文件用EditPlus打開,然后選擇Document(文件),再選擇Convert Encoding(編碼轉換)成UTF-8即可。將修改過編碼格式的類,拷到項目中,覆蓋原先的類。
-
簡單方法,在AS右下角,將編碼改為GBK,再轉為UTF-8,可以解決。
-
Android Studio出現
Error:No service of type Factory available in ProjectScopeServices.
出現場景:
當你從第三方download一個項目時,可能用Android Studio去打開想運行下看看demo的運行效果,結果很不爽,碰到個error:No service of type Factory available in ProjectScopeServices.
解決辦法:
定位到根目錄(也就是Project)下的build.gradle
文件中的buildscript
—>dependencies
中添加
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
編譯下應該就OK了。
具體如下:buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' } }
-
使用Genymotion調試出現錯誤INSTALL_FAILED_CPU_ABI_INCOMPATIBLE。
出現場景:
在Android模擬器上安裝apk調試程序時,出現以下異常:Installation failed with message INSTALL_FAILED_NO_MATCHING_ABIS. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing. WARNING: Uninstalling will remove the application data! Do you want to uninstall the existing application?
調查原因:
由于程序中使用了native libraries(也就是.so文件) 。該native libraries 不支持當前的cpu的體系結構。
現在安卓模擬器的CPU/ABI一般有三種類型,INTEL X86,ARM,MIPS。
解決方案:
根據當前模擬器的cpu體系結構在程序目錄中提供不同目錄下的so文件,如果程序中只提供了ARM,出現INSTALL_FAILED_NO_MATCHING_ABIS的錯誤,那么就改用ARM,否則反之。再或者你全部添加,如下圖:
image.png -
Android 7.0調用系統相機拍照崩潰問題
場景分析:
之前也許我們認為Android 6.0以后加了動態獲取權限問題,我們加了對應權限問題然后調用下面代碼就可以調用系統相機拍照。File tempFile = new File(photoFile, pickPicName); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 從文件中創建uri Uri uri = Uri.fromFile(tempFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, CAMERA_REQUSTCODE);
上面代碼是常見的打開系統相機拍照的代碼,拍照成功后,照片會存儲在tempFile文件中。這段代碼在Android 7.0系統之前使用沒有問題,可以正常調用系統相機拍照問題,但如果運行在7.0系統上就會崩潰,拋出
android.os.FileUriExposedException
異常。
調查原因:
Android 7.0系統強制啟用了被稱作 StrictMode的策略,禁止不安全路徑被外部訪問(禁止向您的應用外公開 file://URI),若要在應用間共享文件,您應發送一項 content://URI,并授予 URI 臨時訪問權限。進行此授權的最簡單方式是使用 FileProvider類。如需了解有關權限和共享文件的詳細信息,請參閱共享文件。
解決方案:
下面就針對7.0系統采用共享文件的形式:content:// URI去調用系統相機拍照,完整代碼如下:File tempFile = new File(photoFile, pickPicName); //獲取系統版本 int currentapiVersion = android.os.Build.VERSION.SDK_INT; // 激活相機 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (currentapiVersion < Build.VERSION_CODES.N) { // 從文件中創建uri Uri uri = Uri.fromFile(tempFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); } else { //兼容android7.0 使用共享文件的形式 ContentValues contentValues = new ContentValues(1); contentValues.put(MediaStore.Images.Media.DATA, tempFile.getAbsolutePath()); Uri uri = AssistantApplication.getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); } startActivityForResult(intent, CAMERA_REQUSTCODE);
-
List集合中contains方法總是返回false。
出現場景:
在利用ArrayList類的caontains方法時,新建一個User類(一個Javabean),然后寫了一個存放User類的ArrayList 但在調用list.contains(user)時總是返回false。
調查原因:
查看ArrayList的源碼,其contains方法源碼如下:/** * Searches this {@code ArrayList} for the specified object. * * @param object * the object to search for. * @return {@code true} if {@code object} is an element of this * {@code ArrayList}, {@code false} otherwise */ @Override public boolean contains(Object object) { Object[] a = array; int s = size; if (object != null) { for (int i = 0; i < s; i++) { if (object.equals(a[i])) { return true; } } } else { for (int i = 0; i < s; i++) { if (a[i] == null) { return true; } } } return false; }
發現在contains方法會調用 object.equals(a[i])方法,其中a[i]是個Object類的實例。也就是說我在調用list.contains(user)時實際上比較的是user.equals(a[i])。也就是這里一直返回false。下面我們來通過拓展知識
"=="和equals方法究竟有什么區別?
來看一下具體什么原因導致的。
拓展知識:"=="和equals方法究竟有什么區別?可參考【簡單解讀equals()與“==”、hashcode()的關系】。-
==
操作符專門用來比較兩個變量的值是否相等
,也就是用于比較變量所對應的內存中所存儲的數值是否相同,要比較兩個基本類型的數據或兩個引用變量是否相等,只能用==操作符
。
如果一個變量指向的數據是對象類型的,那么,這時候涉及了兩塊內存,對象本身占用一塊內 存(堆內存
),變量也占用一塊內存,例如Objet obj = new Object();變量obj是一個內存,new Object()是另一個內存(堆內存
),此時,變量obj所對應的內存中存儲的數值就是對象占用的那塊內存(堆內存
)的首地址
。對于指向對象類型的變量,如果要比較兩個變量是否指向同一個對象,即要看這兩個變量所對應的內存中的數值是否相等,這時候就需要用==
操作符進行比較。 -
equals方法是用于比較兩個獨立對象的內容是否相同
,就好比去比較兩個人的長相是否相同,它比較的兩個對象是獨立的。例如,對于下面的代碼:
String a=new String("foo");
String b=new String("foo");
兩條new語句創建了兩個對象,然后用a/b這兩個變量分別指向了其中一個對象,這是兩個不同的對象,它們的首地址是不同的,即a和b中存儲的數值(對應對象的首地址)是不相同的
,所以,表達式a==b將返回false,而這兩個對象中的內容是相同的,所以,表達式a.equals(b)將返回true。
如果一個類沒有自己定義equals方法(就比如之前創建的User類),那么它將繼承Object類的equals方法,Object類的equals方法的實現源代碼如下:
public boolean equals(Object o) { return this == o; }
這說明,如果一個類沒有自己定義equals方法,它默認的equals方法(從Object類繼承的)就是使用
==
操作符,也是在比較兩個變量指向的對象是否是同一對象,這時候使用equals和使用==會得到同樣的結果,如果比較的是兩個獨立的對象則總返回false。
現在再回過頭來看看user.equals(a[i])
為什么一直返回false,因為這里的a[i]和user是兩個獨立的對象,并且User類也是繼承Object類的equals方法,所以一直返回false。
解決方案:
如果希望能夠比較該User類創建的兩個實例對象的內容是否相同,那么必須在User類中覆蓋equals方法(不再是繼承Object類的equals方法)即可。代碼如下:public boolean equals(Object obj) { if (obj instanceof User) { User u = (User) obj; return this.username.equals(u.username) && this.password.equals(password); } return super.equals(obj); }
-
-
ScrollView嵌套RecyclerView出現item顯示不全的問題
解決方案:
在RecyclerView上再嵌套一層RelativeLayout然后添加屬性android:descendantFocusability="blocksDescendants"
.
該屬性android:descendantFocusability的含義是:當一個view獲取焦點時,定義ViewGroup和其子控件兩者之間的關系。
它一共有3個屬性值,它們分別是:beforeDescendants:viewGroup會優先子類控件而獲取焦點 afterDescendants:viewGroup只有當子類控件不需要獲取焦點的時候才去獲取焦點 blocksDescendants:viewGroup會覆蓋子類控件而直接獲取焦點
代碼示例如下:
<ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="blocksDescendants" android:paddingTop="20dp"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_home_config" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false"/> </RelativeLayout> </ScrollView>
-
databinding在android studio2.3版本后不再默認支持使用
出現場景:
由于項目中用到了databinding
,在android studio 2.2.3
上沒有任何問題,然而在android studio 2.3
版本后卻不再默認支持使用,編譯時會提示com.xxx.xxx.databinding不存在的錯誤。
解決方案:
需要在項目的app目錄下的build.gradle
中的dependencies
里面添加apt 'com.android.databinding:compiler:2.3.0'
,此時編譯即可通過,可正常使用。
注意:
在部分手機上(如紅米和小米)報錯如下:Android app installation: Unknown failure (Failure - not installed for 0)
。
解決方案:For Redmi and Mi devices turn off MIUI Optimization and reboot your phone. Settings > Additional Settings > Developer Options > MIUI Optimization
即:將紅米和小米設備關閉MIUI優化功能然后重啟手機,
設置 -> 更多設置 -> 開發者選項 -> 啟用MIUI優化(設置成關閉) -
Android Studio 2.3版本出現警告:
Warning:Using incompatible plugins for the annotation processing: android-apt. This may result in an unexpected behavior.
出現場景:
如果你的應用一些如ButterKnife,dagger等的開源注解框架的流行,那么應該就用了APT,那么什么是APT呢?
APT(Annotation Processing Tool)是一種處理注釋的工具,它對源代碼文件進行檢測找出其中的Annotation,根據注解自動生成代碼。
所以沒有apt,onClick綁定不了監聽。并且Apt工具的作者宣布了不再維護該工具了,而且Android Studio也有了自己的插件,并且可以通過gradle來簡單的配置。
當Android studio升級到2.3及以上版本,Android Gradle 插件升級到2.3.3時,運用apt時就會出現以下警告:
Warning:Using incompatible plugins for the annotation processing: android-apt. This may result in an unexpected behavior.
解決方案:
android-apt是由一位開發者自己開發的apt框架,源代碼托管在這里,隨著Android Gradle 插件 2.2 版本的發布,Android Gradle 插件提供了名為 annotationProcessor 的功能來完全代替 android-apt ,自此android-apt 作者在官網發表聲明最新的Android Gradle插件現在已經支持annotationProcessor,并警告和或阻止android-apt ,并推薦大家使用 Android 官方插件annotationProcessor。annotationProcessor是APT工具中的一種,他是google開發的內置框架,不需要引入,可以直接在build.gradle文件中使用。
如果想替換為annotationProcessor,那就要知道android-apt是如何使用的。- 添加android-apt到Project下的build.gradle中
//配置在Project下的build.gradle中 buildscript { repositories { mavenCentral() } dependencies { //替換成最新的 gradle版本 classpath 'com.android.tools.build:gradle:2.2.3' //替換成最新android-apt版本 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } }
- 在Module中build.gradle的配置
dependencies { compile 'com.jakewharton:butterknife:8.4.0' apt 'com.jakewharton:butterknife-compiler:8.4.0' }
所以想用
annotationProcessor
替代android-apt
。刪除和替換相應部分即可。步驟如下:- 移除配置在Project下的build.gradle中的android-apt。
即移除classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
. - 移除在Module中build.gradle的配置。
即移除apply plugin: 'android-apt'
. - 修改Module中build.gradle中的dependencies中將 apt修改為annotationProcessor。
即如下:dependencies { compile 'com.jakewharton:butterknife:8.4.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' }
此時編譯程序發現上述警告信息已經消失。
擴展知識:- Provided 和annotationProcessor區別。
- annotationProcessor
只在編譯的時候執行依賴的庫,但是庫最終不打包到apk中,編譯庫中的代碼沒有直接使用的意義,也沒有提供開放的api調用,最終的目的是得到編譯庫中生成的文件,供我們調用。 - Provided
Provided 雖然也是編譯時執行,最終不會打包到apk中,但是跟apt/annotationProcessor有著根本的不同。
- annotationProcessor
A 、B、C都是Library。
A依賴了C,B也依賴了C
App需要同時使用A和B
那么其中A(或者B)可以修改與C的依賴關系為ProvidedA這個Library實際上還是要用到C的,只不過它知道B那里也有一個C,自己再帶一個就顯得多余了,等app開始運行的時候,A就可以通過B得到C,也就是兩人公用這個C。所以自己就在和B匯合之前,假設自己有C。如果運行的時候沒有C,肯定就要崩潰了。
也就是說Provided是間接的得到了依賴的Library,運行的時候必須要保證這個Library的存在,否則就會崩潰,起到了避免依賴重復資源的作用。 - 添加android-apt到Project下的build.gradle中
-
關于在fragment切換時應用崩潰的問題,報錯如下:
java.lang.IllegalArgumentException: Wrong state class, expecting View State but received class android.os.Bundle instead. This usually happens when two views of different type have the same id in the same hierarchy. This view's id is id/center_progress_webview. Make sure other views do not use the same id.
出現場景:
在一個主的FragmentActivity中包括FragmentA,FragmentB,FragmentC,FragmentC中有一個自定義的WebView,當FragmentA,FragmentB和FragmentC之間切換時,程序就會崩潰,并報上述的錯誤。
解決方案:
需要重寫自定義WebView中的onRestoreInstanceState函數,修改如下即可:@Override protected void onRestoreInstanceState(Parcelable state) { try { super.onRestoreInstanceState(state); }catch (Exception e) {} state=null; }
-
Android BottomSheetDialog設置背景透明問題的解決辦法
出現場景:
在使用BottomSheetDialog實現底部彈出框時,需要在自定義布局中設置上半部分控件一半為透明,可是怎么在布局中設置都無效,總是沒辦法顯示透明。
解決辦法:**對BottomSheetDialog設置如下屬性 ** mBottomSheetDialog.getWindow().findViewById(R.id.design_bottom_sheet) .setBackgroundResource(R.color.transparent_color); **如果需要去除灰色遮罩層:** mBottomSheetDialog.getWindow().setDimAmount(0f)
-
Iconfont在Android中的使用
阿里提供的Iconfont-國內功能很強大且圖標內容很豐富的矢量圖標庫,提供矢量圖標下載、在線存儲、格式轉換等功能。
如何使用:- 從iconfont平臺選擇要使用到的圖標
- 下載代碼,把iconfont.ttf文件導入到項目中的assets中的iconfont文件夾中
- 用TextView代替ImagerView,找到圖標相對應的 HTML 實體字符碼給textView設置
- textview設置大小跟顏色,圖標的大小顏色也會改變
- 為Textview設置指定的ttf文字
<TextView android:id="@+id/icon_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:textColor="@color/red" android:textSize="50dp"/> //為TextView設置指定ttf文字 Typeface iconfont = Typeface.createFromAsset(getAssets(), "iconfont/iconfont.ttf"); TextView textview = (TextView)findViewById(R.id.icon_text); textview.setTypeface(iconfont);
上述方法可以使用iconfont了,但是每次都給TextView設置指定setTypeface是不是也很繁瑣,而且一直不斷的在讀取iconfont.ttf文字,也很浪費內存,所以就想到封裝一個工具類。代碼如下:
public class FontHelper { public static final String DEF_FONT = "iconfont/iconfont.ttf"; public static final void injectFont(View rootView) { injectFont(rootView, Typeface.createFromAsset(rootView.getContext().getAssets(), DEF_FONT)); } private static void injectFont(View rootView, Typeface typeface) { if (rootView instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) rootView; int childViewCount = viewGroup.getChildCount(); for (int i = 0; i < childViewCount; i++) { injectFont(viewGroup.getChildAt(i), typeface); } } else if (rootView instanceof TextView) { ((TextView) rootView).setTypeface(typeface); } } }
這樣我們每次調用FontHelper.injectFont(textview)就可以了,你可能會說這還不是我想要的,我連這行代碼都不想多寫,那好,接著往下看:我們可以自定義一個TextView然后初始化時setTypeface即刻,代碼如下:
public class TextViewIcon extends AppCompatTextView { public TextViewIcon(Context context) { super(context); init(context); } public TextViewIcon(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public TextViewIcon(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { setTypeface(Typeface.createFromAsset(context.getAssets(),"iconfont/iconfont.ttf")); } }
現在我們在布局文件中寫如下代碼即可:
<com.xxx.xxx.TextViewIcon android:id="@+id/icon_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:textColor="@color/red" android:textSize="50dp"/>
可是我也想實現像普通textview動態改變文字一樣動態改變iconfont的效果怎么辦呢?也就是說在代碼里動態的調整圖標的大小顏色或改變圖片,iconfont改變大小顏色這很簡單直接調用TextView的setTextSize和setTextColor就可以了,動態設置圖片是不是setText呢?
textview.setText("");
你會發現這并不會如你所愿顯示對應的圖片效果,因為這里涉及到unicode 字符的問題。所以將"&#x" 替換成 "\u",用 unicode 字符來表示,即代碼如下:
textview.settext("\ue66e");
-
Scrollview 自動滾動到頂部或者底部
- 設置默認滾動到頂部
scrollView.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub scrollView.fullScroll(ScrollView.FOCUS_UP); } });
- 設置默認滾動到底部
scrollView.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub scrollView.fullScroll(ScrollView.FOCUS_DOWN); } });
-
在對原有項目重構時,進行代碼遷移,涉及到so文件代碼中報下面錯誤:
java.lang.UnsatisfiedLinkError: dlopen failed: /data/app/com.aihuishou.qualitycheckphotos-1/lib/arm/libzbarjni.so: has text relocations
解決方法:
這個libiconv.so(xx.so)文件使用了較低版本的SDK,當時我的targetSdkVersion為25,所以我就降低到了22,就不會再報錯了,而且能夠正常使用了。 這是libiconv.so文件的解決辦法,如果你用的那個xx.so文件降低到22還報錯的話,建議繼續降低版本嘗試。 -
android studio adb連接不上手機調試。
android stuido 連接真機能運行但是不能調試,通常跟某些手機軟件有關。-
連不上手機時先查看端口是否能被綁定,使用cmd命令
adb nodaemon server
如果提示下面問題,是端口綁定失敗:error: could not install *smartsocket* listener: cannot bind to 127.0.0.1:5037: 通常每個套接字地址(協議/網絡地址/端口)只允許使用一次。
則繼續查看到底是哪個端口給占用了,運行命令:
netstat -ano | findstr "5037"
,結果輸出如下:
D:\software\AndroidTools\sdk\platform-tools>netstat -ano | findstr "5037" TCP 127.0.0.1:5037 0.0.0.0:0 LISTENING 16636 TCP 127.0.0.1:5037 127.0.0.1:61903 TIME_WAIT 0 TCP 127.0.0.1:5037 127.0.0.1:61904 TIME_WAIT 0 TCP 127.0.0.1:5037 127.0.0.1:61912 TIME_WAIT 0 TCP 127.0.0.1:5037 127.0.0.1:61913 TIME_WAIT 0 TCP 127.0.0.1:5037 127.0.0.1:61923 TIME_WAIT 0 TCP 127.0.0.1:5037 127.0.0.1:61924 TIME_WAIT 0 TCP 127.0.0.1:5037 127.0.0.1:61930 TIME_WAIT 0 TCP 127.0.0.1:5037 127.0.0.1:61931 TIME_WAIT 0
說明當前端口被pid為16636的進程占用。
- 運行tasklist 查看列表pid為16636的進程,結果如下:
java.exe 15476 Console 1 618,368 K
audiodg.exe 6384 Services 0 13,524 K
PPAdbServer.exe 16636 Console 1 8,624 K
360MobileLoader.exe 16552 Console 1 27,852 K
tangram_cef_renderer.exe 15088 Console 1 40,392 K
chrome.exe 12552 Console 1 132,308 K
cmd.exe 1424 Console 1 2,728 K - 找到對應的進程,直接運行taskkill /pid 16636 或者taskkill /im PPAdbServer.exe,或者打開任務管理器關閉對應進程。
某些流氓軟件進程可能關不掉,可以直接卸載。。。
-
-
在android 4.0.3上對sdcard不能創建目錄或文件。
出現場景:
之前都在android 4.4以后系統測試運行的程序,發現可以在本地創建對應的數據庫文件或者其他日志文件,然而運行到了Android 4.0.3上卻崩潰了,提示我文件目錄找不到,也真是納悶了,沒辦法,碰到問題得解決啊。
調查原因:
剛開始以為自己權限沒有申請,打開manifest發現權限已申請,如下:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
那肯定不是權限的問題了,所以那就是獲取路徑上面出現問題了。
所以這里就要看看/storage/sdcard
,/sdcard
,/mnt/sdcard
三者的區別了。-
/sdcard
是/mnt/sdcard
的符號鏈,指向/mnt/sdcard
, -
/storage/sdcard
是sdcard
的分區……
/sdcard/
:這是一個軟連接,指向以下: -
/mnt/sdcard
(Android < 4.1) -
/storage/sdcard0
(Android 4.1+)
參考以下內容:- /storage/emulated/0/: to my knowledge, this refers to the "emulated MMC" ("owner part"). Usually this is the internal one. The "0" stands for the user here, "0" is the first user aka device-owner. If you create additional users, this number will increment for each.
-
/storage/emulated/legacy/ as before, but pointing to the part of the
currently working user (for the owner, this would be a symlink to /storage/emulated/0/). So this path should bring every user to his "part". -
/sdcard/: According to a comment by Shywim, this is a symlink to...
- /mnt/sdcard (Android < 4.1)
- /storage/sdcard0 (Android 4.1+)
- /storage/sdcard0/: As there's no legacy pendant here (see comments below), the "0" in this case rather identifies the device (card) itself. One could, eventually, connect a card reader with another SDCard via OTG, which then would become /storage/sdcard1 (no proof for that, just a guess -- but I'd say a good one)
解決方案:
將獲取本地SD卡目錄的代碼改成以下:public static String getSDPath() { boolean sdCardExist = Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED); if (sdCardExist) { return Environment.getExternalStorageDirectory().toString(); } else { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1){ File sdPath = new File("/mnt/sdcard2"); if(sdPath != null && sdPath.exists()) { return sdPath.getAbsolutePath(); } } return ""; } }
這里之所以指定目錄為
/mnt/sdcard2
,是因為Android 4.0.3系統的手機- 在無本地內存卡是對應的本地目錄為
/mnt/sdcard2
, - 而當插入內存卡時,對應的內存卡目錄為
/mnt/sdcard
,而手機的本地目錄依然是/mnt/sdcard2
,所以這里硬編碼寫死了,如果有的手機還是沒辦法使用,請自行修改。
-
-
Android Gradle Build Error:Some file crunching failed, see logs for details.
調查原因:
1.構建Gradle的時候,Gradle會去檢查一下是否修改過文件的后綴名;
2.一般大多數是出現在圖片上,.jpg修改成了.png就會出現這個問題;
3.9patch圖片也可能出現這個問題。
解決方案:
image.png -
從Git上下載項目時報錯如下:
Error:Failed to open zip file. Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.) Re-download dependencies and sync project (requires network) Re-download dependencies and sync project (requires network)
調查原因:
問題出在gradle-wrapper.properties上。我這里配置的是distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
項目中使用了gradle-4.1-all.zip,而gradle-4.1-all.zip無法直接在studio中下載下來。
解決方案:- 直接去http://services.gradle.org/distributions/找到gradle-4.1-all.zip下載下來,放在類似下面的目錄中C:\Users\Administrator.gradle\wrapper\dists\gradle-4.1-all\目錄下。
- 直接把
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
中的https改成http,重新編譯。
-
關于報錯
Error:Configuration with name 'default' not found.
的問題
調查原因:
gradle的配置出現錯誤。
解決方案:- 查看setting.gradle文件中的include 'library' 是否存在本地沒有的library,如果存在則刪除。
如:include ':app', ':imagechoose', ':largerimagescaleview'
,項目中不存在largerimagescaleview,
刪除后為include ':app', ':imagechoose'
. - 將app.gradle文件中的
compile project(':largerimagescaleview')
刪除. - 再次編譯即可。
- 查看setting.gradle文件中的include 'library' 是否存在本地沒有的library,如果存在則刪除。
navicat 系列軟件一點擊菜單欄就閃退。
Windows 10裝的是navicat Premium,只要點菜單欄就立馬閃退。 于是刪了重裝,來回裝幾遍依然如此。后來發現,原來是啟動了有道詞典屏幕取詞才會出現這種現象,關了有道就OK了。-
為什么 Android 不允許子線程中訪問 UI 呢?
因為 Android 的 UI 控件并不是線程安全的,如果在多線程中并發訪問可能會導致 UI 控件處于不可預期的狀態,那還有一個問題,為什么系統不對 UI 控件的訪問加上鎖機制呢?缺點有兩個:- 加上鎖機制會讓 UI 訪問的邏輯變得復雜。
- 鎖機制會降低 UI 訪問的效率,因為鎖機制會阻塞某些線程的執行。
-
調用adapter.notifyDataSetChanged()方法刷新列表時不起作用
分析方案:- 首先先檢查數據是否改變了,在數據源頭debug ,
- 1.1 確定List<object> lists中的這個list有沒有新new過,不能新new,要用notifyDataSetChanged()的話,更新數據一定要保持與之前的setAdapter(,lists)中的lists是同一個對象。
- 1.2 lists的更新數據不能直接賦值,比如不能用lists=lists2,要用 lists.addAll(lists2)。
2.在數據正確的情況下,然后在檢查,notifyDataSetChanged()的執行位置是否在數據改變完之前?
3.更新數據的是否在UI線程中,如果不在把adapter.notifyDataSetChanged()放在UI線程中 。 - 首先先檢查數據是否改變了,在數據源頭debug ,
Android studio中使用adb連接設備時報錯
unable to connect to 172.16.0.26:5555: cannot connect to 172.16.0.26:5555: 由于目標計算機積極拒絕,無法連接。 (10061)
解決方案:
1、第一步:Android設備開啟USB調試,并且通過USB線連接到電腦。
2、第二步:在終端執行以下命令”adb tcpip 5555“。(重啟端口)
3、第三步:在終端執行以下命令”adb connect 172.16.0.26“(172.16.0.26為Android設備的IP地址)。此時拔出USB線,應該就可以adb通過wifi調試Android設備。-
Android WebView clearHistory 失效的解決方案
應用場景:
在當前頁面loadUrl(A),然后為了復用WebView,直接loadUrl(B),此時點擊返回鍵會返回到A頁面(想實現效果是不到A頁面,直接退出),這就使用了WebView的clearHistory()方法,不料WebView的clearHistory()方法并未起作用。
調查原因:
原因是WebView的clearHistory()有個奇怪的特性,那就是只清除當前頁之前的歷史記錄。假設當前頁面為A,我調用WebView的clearHistory()然后loadUrl(B),接著回退還是會退到A。所以正確的調用時機是在B完全載入之后才行。
解決方案:
在webview中使用WebViewClient類來控制URL加載,其中有個方法onPageFinished(WebView view, String url),可在該方法中添加WebView的clearHistory()方法。代碼如下:mWebView.loadUrl("B") mWebView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @Override public void onPageFinished(WebView view, String url) { mWebView.clearHistory(); super.onPageFinished(view, url); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } });
-
Glide 加載https的圖片.
一、首先在moudle 的build.gradle中添加依賴。compile 'com.squareup.okhttp3:okhttp:3.3.1' compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
二、新建工具類HttpsUtils.
public class HttpsUtils { /** * 獲取OkHttpClient * 設置允許HTTPS * */ public static OkHttpClient getOkHttpClient(InputStream... certificates) { SSLSocketFactory sslSocketFactory = HttpsUtils.getSslSocketFactory(certificates, null, null); OkHttpClient.Builder builder = new OkHttpClient().newBuilder(); builder = builder.sslSocketFactory(sslSocketFactory); builder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); return builder.build(); } public static SSLSocketFactory getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password){ try{ TrustManager[] trustManagers = prepareTrustManager(certificates); KeyManager[] keyManagers = prepareKeyManager(bksFile, password); SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManager trustManager = null; if (trustManagers != null){ trustManager = new MyTrustManager(chooseTrustManager(trustManagers)); } else{ trustManager = new UnSafeTrustManager(); } sslContext.init(keyManagers, new TrustManager[]{trustManager}, new SecureRandom()); return sslContext.getSocketFactory(); } catch (NoSuchAlgorithmException e){ throw new AssertionError(e); } catch (KeyManagementException e){ throw new AssertionError(e); } catch (KeyStoreException e){ throw new AssertionError(e); } } private class UnSafeHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session){ return true; } } private static class UnSafeTrustManager implements X509TrustManager{ @Override public void checkClientTrusted(X509Certificate[] chain, String authType)throws CertificateException{} @Override public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException{} @Override public X509Certificate[] getAcceptedIssuers(){ return new X509Certificate[]{}; } } private static TrustManager[] prepareTrustManager(InputStream... certificates){ if (certificates == null || certificates.length <= 0) return null; try{ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates){ String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); try{ if (certificate != null) certificate.close(); } catch (IOException e){ } } TrustManagerFactory trustManagerFactory = null; trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); return trustManagers; } catch (NoSuchAlgorithmException e){ e.printStackTrace(); } catch (CertificateException e){ e.printStackTrace(); } catch (KeyStoreException e){ e.printStackTrace(); } catch (Exception e){ e.printStackTrace(); } return null; } private static KeyManager[] prepareKeyManager(InputStream bksFile, String password){ try{ if (bksFile == null || password == null) return null; KeyStore clientKeyStore = KeyStore.getInstance("BKS"); clientKeyStore.load(bksFile, password.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, password.toCharArray()); return keyManagerFactory.getKeyManagers(); } catch (KeyStoreException e){ e.printStackTrace(); } catch (NoSuchAlgorithmException e){ e.printStackTrace(); } catch (UnrecoverableKeyException e){ e.printStackTrace(); } catch (CertificateException e){ e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } catch (Exception e){ e.printStackTrace(); } return null; } private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers){ for (TrustManager trustManager : trustManagers){ if (trustManager instanceof X509TrustManager){ return (X509TrustManager) trustManager; } } return null; } private static class MyTrustManager implements X509TrustManager{ private X509TrustManager defaultTrustManager; private X509TrustManager localTrustManager; public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException { TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); var4.init((KeyStore) null); defaultTrustManager = chooseTrustManager(var4.getTrustManagers()); this.localTrustManager = localTrustManager; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException{} @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException{ try{ defaultTrustManager.checkServerTrusted(chain, authType); } catch (CertificateException ce){ localTrustManager.checkServerTrusted(chain, authType); } } @Override public X509Certificate[] getAcceptedIssuers(){ return new X509Certificate[0]; } } }
三、在application中初始化即可。
Glide.get(this).register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(HttpsUtils.getOkHttpClient()));
此時glide即可完成對https的支持
Glide.with(this).load("https://").into(img);