Android小提示四
目錄
- 【56.Android P 9.0網絡權限http】
- 【57.布局分包】
- 【58.mHandler在activity警告 --】
- 【59.shape劃線注意事項】
- 【60.異或加解密】
- 【61。onPageScrollStateChanged的三個狀態】
- 【62.Android轉場動畫】
- 【63.矢量圖簡介Vector】
- 【64.權限請求6.0 M 以上動態】
- 【65.RecyclerView刷新固定控件】--避免圖片閃爍
- 【66.判斷主子線程】
- 【67.重用布局 】
- 【67.5. Parcelable和Serializable的效率對比】
【56.Android P 9.0網絡權限http】
解決方法:共四種
1、 如果一定要使用明文通信的話,則可以打開AndroidManifest.xml 文件,在 application 元素中添加:
android:usesCleartextTraffic=“true”
android:usesCleartextTraffic=”false”
此聲明指示該應用不使用明文網絡通信,并使 Android Marshmallow 的平臺網絡堆棧禁止該應用中的明文通信。例如,如果您的應用意外嘗試通過 HTTP 明文請求登錄用戶,該請求將被阻止,該用戶的身份和密碼信息不會泄露到網絡上。
2、 項目改用https請求;
3、 項目的targetSdkVersion改為27以下;
4、 在res的xml目錄下,新建一個xml文件(名稱自定義,如 network_security_config.xml),內容如下:
在manifest清單文件配置application:
<application ...android:networkSecurityConfig="@xml/network_security_config" .../>【57.布局分包】
模塊build.gradle下添加代碼,然后每個文件夾下需要有layout文件夾
android {。。。sourceSets {main{res.srcDirs=["src/main/res", //這個意思是全部資源,包括mipmap等,必須"src/main/res/layout","src/main/res/layout/practice4","src/main/res/layout/practice3",]}} }【58.mHandler在activity警告 --】
其一:(Activity中)
private final MHandler mHandler = new MHandler(this);private static class MHandler extends Handler {private final WeakReference<MainActivity> mActivity;public MHandler(MainActivity activity) {mActivity = new WeakReference<MainActivity>(activity);}@Overridepublic void handleMessage(Message msg) {MainActivity activity = mActivity.get();if(activity!=null){}} }不規范的寫法:private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {};}; //另外一個辦法private Handler mHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {//但是不能引用外部方法了return false;}});其二:(自定義View中)
private TimerHandler mHandler;//采用弱引用防止內存泄漏 private static final class TimerHandler extends Handler {private WeakReference<StudyView> mView;private TimerHandler(StudyView clockView) {mView= new WeakReference<>(clockView);}@Overridepublic void handleMessage(Message msg) {StudyView view = mView.get();//isPlaying是StudyView里的變量if (view != null && view.isPlaying) {view.getTime(); //StudyView里的方法view.invalidate();//重新繪制sendEmptyMessageDelayed(1, 1000);//每1000毫秒一請求}} } 原文鏈接:https://blog.csdn.net/qq_38363506/article/details/90903240【其他警告】
@SuppressLint("SimpleDateFormat") SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-ddHH:mm:ss"); //解決 SimpleDateFormat newSimpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日HH時mm分", Locale.getDefault()); @SuppressWarnings(“rawtypes”)和@SuppressWarnings({ “unchecked”, “rawtypes” }) 不規范寫法:Class clazz = Class.forName(“android.view.Display”); 正確寫法:Class<?> clazz = Class.forName("android.view.Display");【59.shape劃線注意事項】
line劃線時注意一下幾點:
- 只能畫水平線,畫不了豎線;
- 線的高度是通過stroke的android:width屬性設置的;
- size的android:height屬性定義的是整個形狀區域的高度;
- size的height必須大于stroke的width,否則,線無法顯示;
- 線在整個形狀區域中是居中顯示的;
- 線左右兩邊會留有空白間距,線越粗,空白越大;
- 引用虛線的view需要添加屬性android:layerType,值設為"software",否則顯示不了虛線。
【60.異或加解密】
//java.test里測試 加密與解密的代碼相同 int pwd = 9; String normal = "~!@#$%^&*()_+=-0"; //加密 int len = normal.length(); StringBuilder bui=new StringBuilder();for(int i=0;i<len;i++){System.out.println("char:"+normal.charAt(i));int res=normal.charAt(i)^pwd;bui.append((char)res); //強轉為ASCII字符 } System.out.println("加密后:"+bui.toString());System.out.println("-------下面代碼跟上面一樣--------------");String cry=bui.toString();//解密 int len = cry.length(); StringBuilder bui=new StringBuilder();for(int i=0;i<len;i++){System.out.println("char:"+cry.charAt(i));int res=cry.charAt(i)^pwd;bui.append((char)res); //強轉為ASCII字符 } System.out.println("解密后:"+bui.toString());【61。onPageScrollStateChanged的三個狀態】
viewPager.addOnPageChangeListener(new ViewPager.OnPagerChangeListener(){public void onPageScrolled(int position, float offset, int pix){//參數2:偏移量0-1,滑動到一半可以用0.5 標識//參數3:分辨率,比如1080P的,則就是0-1079}public void onPageSelected(int position){//停止滑動后的位置}public void onPageScrollStateChanged(int arg0){參數arg0有三種取值:0:什么都沒做1:開始滑動2:滑動結束打印了一下滑動過程的順序:從滑動開始依次為:argo== (1,2,0)} });【62.Android轉場動畫】
https://www.jianshu.com/p/86ba2e1eb80c
【63.矢量圖簡介Vector】
1.兩種方法來創建:
- 1)右擊drawable–>Drawable resource file–>設置root
element為vector,這樣的矢量圖繪制邏輯完全掌握在開發者手里(自己繪制–》看下面的); - 2)右擊drawable–>Vector
Asset,選擇SVG或者PSD文件直接生成根標簽為vector的xml文件,怎樣把png轉換成SVG(可以用 阿里iconfont 或者
http://inloop.github.io/svg2android/)。
作者:宛丘之上兮
鏈接:https://www.jianshu.com/p/0972a0d290e9
- width和height:當使用這個矢量圖的View的寬高是wrap_content 的時候這兩個屬性才生效;
- ewportWidth和viewportHeight:決定畫布的寬高,是定義的一個虛擬空間,方便編輯pathData屬性,如果pathData中的點超出了這個虛擬空間,超出的部分將不會展現給用戶;虛擬空間的原點仍然還是在左上角(R點就是原點)。
簡單的vector:https://blog.csdn.net/qq_35323561/article/details/80018898
【64.權限請求6.0 M 以上動態】
//1.簡單寫法 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){//兩種方式String[] perms = {"android.permission.RECORD_AUDIO",Manifest.permission.WRITE_EXTERNAL_STORAGE};//因為是在Activity下,所以可以直接調用if(checkSelfPermission(perms[0]) == PackageManager.PERMISSION_DENIED ||checkSelfPermission(perms[1]) == PackageManager.PERMISSION_DENIED){//自己在onRequestPermissonsResult處理requestPermission(perms, 200);//ActivityCompat.requestPermissions(this,perms,200);} else {....} }//2.分開 [自己去封裝工具類] //應用 if(checkPermission()){//用戶擁有權限... } else {//去請求requestPermission(); }//檢查權限 private boolean checkPermission(){int result = ContextCompat.checkSelfPermission(this,Manifest.permission.RECORD_AUDIO);//如果權限授予了if(result == PackageManager.PERMISSION_GRANTED){return true;} else {return false;} } //請求權限 private void requestPermission(){//第一次被拒后 或者之前允許又在設置去掉了 走這里,所以仍要請求權限(但是可以彈窗說名原因)if(ActivityCompat.shouldShowRequestPermissionRationale(this,perms[0])){toast("請在設置里允許權限");//我強制在彈窗請求ActivityCompat.requestPermission(this,new String[]{perms[0],perms[1]}, 1);} else {//請求權限(兩種情況)//第一種:第一次請求權限ActivityCompat.requestPermission(this,new String[]{perms[0],perms[1]}, 1);//第二種:拒絕并選擇“Never ask again”} } //接受權限結果 @override public void onRequestPermissonsResult(...){if(requestCode == 1){if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){//申請成功。。。邏輯代碼} else {//用戶拒絕}return;}super.onRequestPermissonsResult(...); }shouldShowRequestPermissionRationable 方法會返回以下兩種情況:
- 返回true:
用戶之前在申請權限操作時,點擊了“拒絕”按鈕,但是沒有選中“Never ask again”選項。
處理方法—— 再次調用requestPermission方法申請權限。 - 返回false:
- 用戶從來沒有申請過此權限;
處理方法—— 直接調用 requestPermission方法申請權限。 - 用戶之前選中拒絕,并勾選了“Never ask again”選項。
處理方法—— 彈出自定義對話框,提示用戶此操作必須通過權限申請之后才能繼續使用此功能,并給用戶提供進入權限設置界面的入口。
- 用戶從來沒有申請過此權限;
注意: shouldShowRequestPermissionRationable 返回true的情況
在國內很多手機廠商中設置了自動屏蔽,也就是沒有返回true的情況,比如華為、小米等手機。
外例
public void requestPerm(View view){//版本高于23,需要動態申請if(shouldAskPerm){//判斷是否已經授予權限if(ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_CONTACTS)!= PackageManager.PERMISSION_GRANTED){//調用shouldShowRequestPer...判斷用戶之前的操作if(ActivityCompat.shouldShowRequestPR(this,Manifest.permission.WRITE_CONTACTS)){//用戶再對話框中拒絕權限,并沒有選中“Never ask again”ActivityCompat.requestPermission(this,new String[]{Manifest.permission.WRITE_CONTACTS},REQUEST_CODE);}else{//第一種:第一次請求權限ActivityCompat.requestPermission(this,new String[]{perms[0],perms[1]}, 1);//第二種:拒絕并選擇“Never ask again”//這里需要再SharedPreference里設置第一次申請的操作,默認true,第一次申請后false//跳轉到設置里,去手動設置允許}} else{//權限已申請,執行操作。。。}} else{//版本低于高于23} } public boolean shouldAskPerm(){return Build.VERSION.SDK_INT>=Build.VERSION_CODES.M; }checkSelfPermission 檢查某權限是否已申請 requestPermissions 主動發送權限申請 shouldShowRequestPermissionRationale 判斷用戶之前對申請權限做出的動作【65.RecyclerView刷新固定控件】–避免圖片閃爍
https://blog.csdn.net/qq402164452/article/details/53464091
//注意這是三個參數的 onBindViewHolder @Override public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {//為空時,就是最初的,初始化if (payloads.isEmpty()) {onBindViewHolder(holder, position);} else {holder.container.setBackgroundColor(position == mCurrentPosition ? ContextCompat.getColor(activity,R.color.color_FF35BAF3) : Color.TRANSPARENT);} }【66.判斷主子線程】
onCreate->onStart->onPostCreate->onResume->onPostResume 到onPostCreate時,Activity應該已經徹底跑起來了,這時可以測量View寬高讓View重繪, 需要先判斷當前線程到底是不是主線程, 然后根據判斷結果來決定到底是調用 invalidate() 還是 postInvalidate() 方法. 如果當前是主線程, 就調用 invalidate() 方法; 而如果當前是子線程, 就調用 postInvalidate() 方法, 注意: 子線程中不能調用 invalidate() 方法, 否則就會報異常, 提示我們不能在子線程中更新UI
//1. public boolean isMainThread() {return Looper.getMainLooper() == Looper.myLooper(); } //2. public boolean isMainThread() {return Looper.getMainLooper().getThread() == Thread.currentThread(); } //3. public boolean isMainThread() {return Looper.getMainLooper().getThread().getId() == Thread.currentThread().getId(); }Android中切換到主線程更新方法:
?? View.post()方法在android7.0之前,可能會不生效,在異步線程view.post方法不執行的情況居多。建議使用Handler post方法代替。但Android 7.0之后不管在主線程還是在子線程都可以成功執行view.post內部邏輯(https://blog.csdn.net/longlong2015/article/details/88826269)
??在Android 7.0之后的手機上如果通過new創建的View,如果沒有將它通過addView()加入到ViewGroup布局中,那通過View.post()發送出去的任務將不再執行,也就無法通過Viwe.post更新UI。
【67.重用布局 】
注意點:
< include>
- 重寫layout_*的屬性記得先重寫 android:layout_height 和android:layout_width。
- include如果指定了id,那么layout屬性的根視圖id會被強制修改成include中的id,如果不注意很容易出現空指針問題。
< merge>
- 復用在LinearLayout和RelativeLayout中會有不同的表現,在前者會以線性的方式布局,后者delete按鈕會遮擋add按鈕,所以使用merge標簽一定要注意實際的根視圖類型
- merge必須放在布局文件的根節點上
- merge并不是一個ViewGroup,也不是一個View,它相當于聲明了一些視圖,等待被添加。
- 因為merge標簽并不是View,所以在通過LayoutInflate.inflate方法渲染的時候, 第二個參數必須指定一個父容器,且第三個參數必須為true,也就是必須為merge下的視圖指定一個父親節點。
- 因為merge不是View,所以對merge標簽設置的所有屬性都是無效的
- 如果Activity的布局文件根節點是FrameLayout,可以替換為merge標簽,這樣,執行setContentView之后,會減少一層FrameLayout節點。
- 自定義XXXLayout控件時,如果使用LayoutInflater.inflate(R.layout.xxx, this, true)填充視圖,那么該布局的根元素最好設置成,這一點其實是和上一點相同的,有助于直接減少視圖層級。
< ViewStub> 懶加載View
你的布局中可能存在很少情況下才用到的復雜布局,比如單條詳情、進圖條或者是一些撤銷消息等等,這些布局可以只在你需要的時候才加載以提升布局的渲染速度。
定義ViewStub
ViewStub 是一個輕量級的視圖,它不參與繪制也不參與任何的布局工作。因此,它在視圖層級的構建中消耗的資源是非常小的。每一個ViewStub在使用時只需要通過android:layout去定義它需要加載布局文件即可。
下面給出的ViewStub承載了一個透明的進度條,它只在特定情況下才需要展現給用戶。
<ViewStubandroid:id="@+id/stub_import"android:inflatedId="@+id/panel_import"android:layout="@layout/progress_overlay"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_gravity="bottom" />加載ViewStub布局
當我們需要讓ViewStub承載的視圖展現時,只需要通過調用setVisibility(View.VISIBLE)或者inflate()方法即可
一旦ViewStub被可見或者被布局了,那么它就從視圖層級中剝離出來,取代ViewStub存在于視圖層級的是android:layout屬性所指定的布局,該布局的id可以通過android:inflatedId指定。
這里和include一樣,android:inflatedId屬性也會覆蓋layout中根視圖的id。
注意點
- ViewStub只能被inflate一次,多次調用會出異常。第一次setVisibility(View.Visibility)會被動調用一次inflate,因此需要注意。
- ViewStub被inflate之后會從視圖層級中移除,因此再次調用findViewById嘗試獲取ViewStub對象會返回空,不要嘗試使用該對象,否則會出現空指針。
- ViewStub中layout_*屬性都是為新加載的視圖的根視圖設置的,與 < include > 標簽一樣,ViewStub加載的根視圖自身的layout_*屬性會被ViewStub重寫。比如layout_height,它不能指定ViewStub本身的高度,因為ViewStub本身的高度和寬度都是0,它指定的其實是需要加載的布局的根視圖高度。又由于此,在布局時要注意基于ViewStub的相對布局在ViewStub未inflate之前,位置與實際位置是有偏差的。
- 一般xml文件中定義的屬性都可以通過代碼設置,同樣ViewStub也可以通過方法setLayoutResource在代碼中動態設置應該加載的layout文件,此時一個ViewStub就可以根據邏輯不同使用不同的視圖。
【67.5. Parcelable和Serializable的效率對比】
Parcelable和Serializable的效率對比 Parcelable vs Serializable 號稱快10倍的效率.
- 內存序列化上選擇Parcelable,
- 存儲到設備或者網絡傳輸上選擇Serializable(當然Parcelable也可以但是稍顯復雜)
【待測試】
//寫在android下面 applicationVariants.all { variant ->variant.outputs.all {outputFileName = "MyAppName${variant.versionName}_${releaseTime()}_${variant.name}.apk"} }//放到build里面跟apply同級就行了。 static def releaseTime() {SimpleDateFormat str = new SimpleDateFormat("yyyy_MM_dd_KK_mm")return str.format(new Date()) }總結
以上是生活随笔為你收集整理的Android小提示四的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度和知乎哪个引流效果好?知乎和百度的有
- 下一篇: linux搭建邮件服务器