Android知識點

開發tip

  • 用getApplication()來取Context當參數 對於須要使用Context對象做爲參數的函數,要使用getApplication()獲取Context對象當參數,而不要使用this,除非你須要特定的組件實例!getApplication()返回的Context是屬於Application的,它會在整個應用的生命週期內存在,遠大於某個組件的生命週期,因此即便某個引用長期持有Context對象也不會引起內存泄露。好比toast
  • 或許咱們較爲習慣 if(object == null); 這種寫法,對於直接對象的判斷天然是沒有問題的。可是若是遇到對象嵌套,譬如 if(object.child == null) 的狀況,若是一開始 object 就是個 null 對象,可想而知程序在執行條件語句判斷時就會報 NullPointerException 異常。固然你能夠先判斷 object 再判斷 child,但與其這樣不如一開始就習慣 if(null == object.child); 來得更方便些,equals() 同理。
  • 頻繁操做的文件建議考慮使用緩衝提升程序性能,intent不要傳遞大量數據,使用持久化數據來處理(存在文件中),不要在application中緩存數據
  • 在涉及網絡的操做中,必需要考慮到沒有網絡和信號差等狀況
  • 一些華爲機器log和Toast打印一直異常,不要在這上面耽誤時間
  • 代碼至少天天備份一次,或者是完善一個功能就備份一次,不要堆積以後一次性備份,由於在你的代碼出問題須要回溯代碼時你須要從服務器上從新取代碼,同時也能夠避免代碼不是最新致使最後和其餘人合併時不知道改了哪些地方
  • 注意:有些開發者可能也發現了,若是咱們須要一個圓形的ImageButton的話,其實,咱們沒有必要本身寫。若是ImageButton的圖標是固定不變的,咱們徹底可讓設計師給我設計一個圓形的圖片,而後直接設置再ImageButton上就能夠了。
  • 同步方法:SDK裏大部分方法都爲同步方法,即這個方法執行完畢,纔會走後面的代碼。異步方法:帶有callback以及api註釋裏明確寫明異步方法的方法,即不須要等這個方法走完,後邊的代碼就已經在執行了,經過callback獲得方法執行的結果。
  • 很多比較有經驗的Android程序員可能都遇到過這個狀況,就是當你的項目變得愈來愈大,有的時候加載一張drawable-hdpi下的圖片,程序就直接OOM崩掉了,但若是將這張圖放到drawable-xhdpi或drawable-xxhdpi下就不會崩掉,其實就是這個道理。那麼通過上面一系列的分析,答案天然也就出來了,圖片資源應該儘可能放在高密度文件夾下,這樣能夠節省圖片的內存開支,而UI在設計圖片的時候也應該儘可能面向高密度屏幕的設備來進行設計。就目前來說,最佳放置圖片資源的文件夾就是drawable-xxhdpi。那麼有的朋友可能會問了,不是還有更高密度的drawable-xxxhdpi嗎?幹嘛不放在這裏?這是由於,市面上480dpi到640dpi的設備實在是太少了,若是針對這種級別的屏幕密度來設計圖片,圖片在不縮放的狀況下自己就已經很大了,基本也起不到節省內存開支的做用了。
  • 不能在 Activity 沒有徹底顯示時顯示PopupWindow和Dialog。例如在activity的onCreate方法裏面調用popupwindow的show方法,有可能因爲activity沒有徹底初始化致使程序異常(android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid),若是非要在一進activity就顯示popupwindow,用handler.post、View.postDelay來處理
  • 全部的圖片務必先用http://tinypng.com壓一遍用
  • JPG 代替 PNG,因爲 JPG 沒有 Alpha 通道,因此文件更小,適用於不須要透明度的圖片能夠考慮。
  • 不一樣Module的資源文件、佈局名不要相同
  • 須要管理相互獨立的而且隸屬於Activity的Fragment使用FragmentManager(),而在Fragment中動態的添加Fragment要使用getChildFragmetManager()來管理,而不是getFragmentManager。
  • 在ScrollView中添加一個Android:fillViewport="true"屬性就能夠了。顧名思義,這個屬性容許 ScrollView中的組件去充滿它。 當ScrollView沒有fillViewport=「true」時, 裏面的元素(好比LinearLayout)會按照wrap_content來計算(不論它是否設了"fill_parent")
  • gson同一字段,數據類型不一樣的字段,類型能夠寫object
  • radiobutton佈局下面倆個button切換時,fragment裏有listview會頂到下面,能夠在佈局初始化完後lv.setFocusable(false)
  • 有一種狀況是手機並不在咱們身邊,咱們也沒法使用調試工具。此時能夠接入一些第三方的日誌記錄工具。在開發狀態下不建議使用友盟 360之類sdk,由於頗有可能咱們的app根本沒法鏈接到網絡就崩潰了。 能夠選擇把日誌存到本地文件中。再由使用手機的人發回來。通常這我的是測試。若是app未接入任何日誌保存工具,能夠在data/anr/目錄下查看到全部的ANR異常信息。但須要su權限。不然沒法訪問到。
  • 在複雜的佈局上,好比不少app的首頁須要加載不一樣類型的item。使用了RecyclerView多類型加載,刷新數據時必定要使用單獨對item刷新api。切勿使用notifyDataSetChanged()方法,這裏要用兩個參數的notifyItemChanged(1,"gfg")方法。
  • 當沒法經過搜索解決問題的時候,讀源碼是最快的解決思路。千萬不要瞎猜和嘗試隨緣寫代碼來解決問題。
  • .9圖的左上表明能夠拉伸的區域,右下表明能夠填充的區域
  • 多語言字符串設置要都配置上,要否則就報android.content.res.Resources$NotFoundException
  • 在8.0上切換語言部分文字沒有切換,緣由是getResources原來是經過application的,改爲activity的就能夠了
  • 找不到問題,能夠二分註釋代碼來定位問題
  • TextView.setTextColor方法不能用R.Color.XXX設置顏色

UI問題

一段文字顏色不一樣點擊不一樣,不想寫多個textview,能夠這樣寫html

String html="僅限<font color=#FF9900>"+offer.getCardGroupName()+"</font>使用";
nrbankTextView.setText(Html.fromHtml(html));

也能夠這樣寫android

String content = "發現" + newGroup.size() + "張新卡片";
int textColor = getResources().getColor(R.color.import_card_red);
SpannableString spannableString = TextStyleUtils.setTextStyle(content, newGroup.size() + "張", textColor);
tv_.setText(spannableString);

也能夠這樣寫:程序員

private void setCurrentTip() {
        StringBuilder actionText = new StringBuilder();
        actionText.append("<a style=\"text-decoration:none;\" href='text' >若平臺今日快速還款額度用完,還款將於第二天到帳\n</a>");
        actionText.append("<a style=\"text-decoration:none;\" href='text' >查看</a>");
        actionText.append("<a style=\"text-decoration:none;\" href='blue' >規則</a>");
        tvBankArrivaldesc.setText(Html.fromHtml(actionText.toString()));
        tvBankArrivaldesc.setMovementMethod(LinkMovementMethod
                .getInstance());
        CharSequence text = tvBankArrivaldesc.getText();
        int ends = text.length();
        Spannable spannable = (Spannable) tvBankArrivaldesc.getText();
        URLSpan[] urlspan = spannable.getSpans(0, ends, URLSpan.class);
        SpannableStringBuilder stylesBuilder = new SpannableStringBuilder(text);
        stylesBuilder.clearSpans();
        for (URLSpan url : urlspan) {
            FeedTextViewURLSpan myURLSpan = new FeedTextViewURLSpan(url.getURL(), getActivity());
            stylesBuilder.setSpan(myURLSpan, spannable.getSpanStart(url),
                    spannable.getSpanEnd(url), spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        tvBankArrivaldesc.setText(stylesBuilder);
    }


    static class FeedTextViewURLSpan extends ClickableSpan {
        private String clickString;
        private Context context;

        public FeedTextViewURLSpan(String clickString, Context context) {
            this.clickString = clickString;
            this.context = context;

        }

        @Override
        public void updateDrawState(TextPaint ds) {
            ds.setUnderlineText(false);//去掉下劃線
            //給標記的部分 的文字 添加顏色
            if (clickString.equals("blue")) {
                ds.setColor(Color.parseColor("#4D8FDD"));
            } else if (clickString.equals("text")) {
                ds.setColor(Color.parseColor("#999999"));
            }
        }

        @Override
        public void onClick(View widget) {
            Intent intent = new Intent();
            // 根據文字的標記 來進行相應的 響應事件
            if (clickString.equals("blue")) {
                ToastUtils.show(context, "規則");
            }
        }
    }

想每次進入頁面刷新,能夠放在onResume方法裏(不推薦)
顏色透明度:#7f000000 表明50%透明度的黑色
web

ImageView的android:adjustViewBounds屬性
http://blog.csdn.net/pingchuanyang/article/details/9252689數據庫

framelayout兩佈局重疊,如何讓下層不響應事件
在layout文件裏本層下增長Android:clickable="true"
在上層佈局的父佈局上增長android:clickable="true"api

給圖片設置背景,例如ui給的圖背景透明的圖片數組

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="#1c69ac" />
            <corners android:radius="5dp"/>
        </shape>
    </item>
    <item
        android:left="15dp"
        android:right="15dp"
        android:top="15dp"
        android:bottom="15dp"
        android:drawable="@drawable/icon_yingwen_fuhao_keyboard_del_default"
         />
</layer-list>

因爲listview的複用機制,當給一個控制設置狀態的時候,相應的要給其餘控件也設置狀態(else),不然就會複用
緩存

 listview若是單獨更新某個控件的時候,能夠更改單個數據的狀態,而後notify
安全

viewpager+fragment服務器

//界面可見時再加載數據
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser{
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            mMyWalletPresenter.reqRanking(position);//[2周排行,1總排行]
        }
    }

CardView
使用android:background設置背景顏色無效。 app:cardBackgroundColor 設置背景顏色

NestedScrollView嵌套RecyclerView滑動卡頓解決方案
若是你APP的API適配的minSdkVersion高於21,直接在RecyclerView中加上android:nestedScrollingEnabled="false"或者禁用recycleview滑動

//解決滑動滑動衝突
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override
            public boolean canScrollVertically() {
                return false;
            }
        };

selector異常
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #3: <item> tag requires a 'drawable' attribute or child tag defining a drawable
產生緣由:個人一個button按鈕的background屬性中設置成"@color/button_text_selector",按照異常來講,這個background這個屬性的值必須是drawable類型的,不能是color類型。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!--<item android:color="@color/iqb_ui_base_color_1004" android:state_enabled="true" />-->
    <!--<item android:color="@color/iqb_ui_base_color_1005" android:state_enabled="false" />-->
    <!--<item android:color="@color/iqb_ui_base_color_1004" />-->

    <item android:state_enabled="true">
        <shape>
            <solid android:color="@color/iqb_ui_base_color_1004" />
        </shape>
    </item>
    <item android:state_enabled="false">
        <shape>
            <solid android:color="@color/iqb_ui_base_color_1005" />
        </shape>
    </item>


</selector>

代碼寫陰影

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 陰影部分 -->
    <!-- 我的以爲更形象的表達:top表明下邊的陰影高度,left表明右邊的陰影寬度。其實也就是相對應的offset,solid中的顏色是陰影的顏色,也能夠設置角度等等 -->
    <item
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        android:top="2dp">
        <shape android:shape="rectangle">

            <gradient
                android:angle="270"
                android:endColor="#0F000000"
                android:startColor="#0F000000" />

            <corners
                android:bottomLeftRadius="6dip"
                android:bottomRightRadius="6dip"
                android:topLeftRadius="6dip"
                android:topRightRadius="6dip" />
        </shape>
    </item>

    <!-- 背景部分 -->
    <!-- 形象的表達:bottom表明背景部分在上邊緣超出陰影的高度,right表明背景部分在左邊超出陰影的寬度(相對應的offset) -->
    <item
        android:bottom="5dp"
        android:left="3dp"
        android:right="3dp"
        android:top="3dp">
        <shape android:shape="rectangle">

            <gradient
                android:angle="270"
                android:endColor="#FFFFFF"
                android:startColor="#FFFFFF" />

            <corners
                android:bottomLeftRadius="6dip"
                android:bottomRightRadius="6dip"
                android:topLeftRadius="6dip"
                android:topRightRadius="6dip" />
        </shape>
    </item>
</layer-list>

單選

public void setClicked(int clicked) {
        mapClassfiyAdapter.setClicked(clicked);
    }

   public void setClicked(int clicked){
        this.clicked=clicked;
        notifyDataSetChanged();
    }

    if(i==clicked){           
            tv_classfiy.setTextColor(mContext.getResources().getColor(R.color.map_pop_click));
        } else {
            tv_classfiy.setTextColor(mContext.getResources().getColor(R.color.white));
        }

多選

// 用來控制CheckBox的選中情況
    private static HashMap<Integer, Boolean> isSelected;


    public MapClassfiyAdapter(Context mContext, List<String> classfiyList) {
        this.mContext = mContext;
        isSelected = new HashMap<Integer, Boolean>();
    }

    // 初始化isSelected的數據
    private void initDate() {
        for (int i = 0; i < classfiyList.size(); i++) {
            getIsSelected().put(i, false);
        }
    }

    public  HashMap<Integer, Boolean> getIsSelected() {
        return isSelected;
    }

    public  void setIsSelected(HashMap<Integer, Boolean> isSelected) {
        MapClassfiyAdapter.isSelected = isSelected;
    }

    @Override
    public View getView(int i, View convertView, ViewGroup viewGroup) {
     ...
        if(getIsSelected().get(i)){
            tv_classfiy.setTextColor(mContext.getResources().getColor(R.color.map_pop_click));
        }else{
            tv_classfiy.setTextColor(mContext.getResources().getColor(R.color.white));
        }


        tv_classfiy.setText(classfiyList.get(i));

      }
      
      ---
      
    public void setClicked(int clicked) {
        isSelected = mapClassfiyAdapter.getIsSelected();
        Boolean aBoolean = isSelected.get(clicked);
        if(aBoolean){
            isSelected.put(clicked,false);
        }else{
            isSelected.put(clicked,true);
        }
        mapClassfiyAdapter.setIsSelected(isSelected);
    }

描邊

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:left="-2dp"
        android:right="-2dp">
        <shape>
            <solid android:color="@color/white" />

            <stroke
                android:width="1.5dp"
                android:color="#80ffffff" />
        </shape>
    </item>

</layer-list>

---

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:right="-2dp"//負數,不描右邊
        >
        <shape
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle">
            <solid
                android:color="#EFEFEF"/>
            <corners
                android:topLeftRadius="10dp"
                android:topRightRadius="0dp"
                android:bottomRightRadius="0dp"
                android:bottomLeftRadius="10dp"
                />
            <stroke
                android:width="0.5px"
                android:color="#505050"/>
        </shape>
    </item>
</layer-list>

popWindow點背景消失

mPopupWindow = new PopupWindow(
                    mPopupView,
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);

            //產生背景變暗效果,佈局背景要改
            // 根佈局背景android:background="#00ffffff"
            backgroundAlpha(0.5f);
            mPopupWindow.setOutsideTouchable(true);
            mPopupWindow.setFocusable(true);
            mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
            mPopupWindow.setOnDismissListener(new PoponDismissListener());

            mMyHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mPopupWindow.showAtLocation(mPopupView, Gravity.CENTER, 0, 0);
                }
            }, 200);

...
   class PoponDismissListener implements PopupWindow.OnDismissListener {
        @Override
        public void onDismiss() {
            backgroundAlpha(1f);
        }
    }

    private void backgroundAlpha(float v) {
        WindowManager.LayoutParams lp = context.getWindow().getAttributes();
        lp.alpha = v; //0.0-1.0
        context.getWindow().setAttributes(lp);
    }

數據庫升級
新增版本號,而後

if (newVersion == 4) {//方法一
                TableUtils.dropTable(connectionSource, CardModel.class,
                        true);
                TableUtils.createTable(connectionSource, CardModel.class);
                return;
            }
            if (oldVersion < 5) {//方法二
                getUserInfoDao().executeRaw("ALTER TABLE `USERINFOTABLE` ADD COLUMN memberaccesstoken TEXT  DEFAULT'';");
                getUserInfoDao().executeRaw("ALTER TABLE `USERINFOTABLE` ADD COLUMN membertype INTEGER DEFAULT 0;");
                return;
            }

數字後加2個點

DecimalFormat  df = new DecimalFormat("########0.00");
crt_pp_money.setText("¥" + df.format(Double.valueOf(money)));

填充字符串

<string name="crt_shift_limit">銀行單筆限額%1$d萬,單日限額號%2$d萬</string>
bankMessage.setText(getResources().getString(R.string.crt_shift_limit,3,5));

其餘問題

webview post
webView.postUrl(mUrl,postdata.getBytes());

webview重定向引發的問題
解決方法:

WebView.HitTestResult hitTestResult = webView.getHitTestResult();
        Log.d("hitTestResult",hitTestResult.getType()+"----"+hitTestResult.getExtra());
        if (hitTestResult.getType() == WebView.HitTestResult.UNKNOWN_TYPE) {//=0發生重定向
            loadUrl();
            return true;
        }else{}

或者

@Override
    public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                mIsPageLoading= false;//重定向是不回執行finished方法的

            }

   public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                mIsPageLoading= true;
            }

webview:顯示404等異常狀態都有回調,能夠顯示一個友好的界面
能夠經過URL是否包含某個字段判斷去哪一個頁面

try-catch 捕獲異常能夠捕獲裏return出去

手機適配

String PhoneName = String.valueOf(android.os.Build.MANUFACTURER);
        if (PhoneName.equals("OPPO")) {
            AutoLogin1.setVisibility(View.INVISIBLE);
            AutoLogin2.setVisibility(View.INVISIBLE);
        }

華爲等虛擬鍵盤遮擋問題

//由於某些機型是虛擬按鍵的,因此要加上如下設置防止擋住按鍵.
  setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

若是你觸發了一個intent,並且沒有任何一個app會去接收這個intent,那麼你的app會crash。爲了驗證是否有合適的activity會響應這個intent,須要執行queryIntentActivities() 來獲取到可以接收這個intent的全部activity的list。若是返回的List非空,那麼你才能夠安全的使用這個intent。例如:

PackageManager packageManager = getPackageManager();
                List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
                boolean isIntentSafe = activities.size() > 0;
                if (isIntentSafe) {
                    startActivity(mapIntent);
                 }

檢查是否有某個權限

public static boolean hasPermissions(Context context, String... perms) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        }

        for (String perm : perms) {
            //PERMISSION_DENIED
            boolean hasPerm = (ContextCompat.checkSelfPermission(context, perm) ==
                    PackageManager.PERMISSION_GRANTED);
            if (!hasPerm) {
                return false;
            }
        }

        return true;
    }
 boolean location = hasPermissions(this, Manifest.permission.ACCESS_COARSE_LOCATION);

跳轉到別的APP

private void doStartApplicationWithPackageName(String packagename) {

        Intent intent = new Intent();
        PackageInfo packageinfo = null;
        PackageManager packageManager = context.getPackageManager();
        try {
            packageinfo = packageManager.getPackageInfo(packagename, 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        if (packageinfo == null) {
            intent.setClass(context, NoLianshuaActivity.class);
            context.startActivity(intent);
            dismiss();
            return;
        }
        intent = new Intent(Intent.ACTION_VIEW,
                Uri.parse("mydemo://go?money=" + money 
                + "&isFromMydemo=" + true));
    }
<activity android:name=".Main2Activity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="mydemo" />
    </intent-filter>
</activity>

另外一個APP

private void parseIntentUrl() {
        Intent intent = getIntent();
        String action = intent.getAction();
        if (Intent.ACTION_VIEW.equals(action)) {
            Uri uri = intent.getData();
            if (uri != null) {
                String host = uri.getHost();
                String dataString = intent.getDataString();
                String id = uri.getQueryParameter("goodsId");
                String path = uri.getPath();
                String path1 = uri.getEncodedPath();
                String queryString = uri.getQuery();
                Log.d("Alex", "host:"+host);
                Log.d("Alex", "dataString:" + dataString);
                Log.d("Alex", "id:" + id);
                Log.d("Alex", "path:" + path);
                Log.d("Alex", "path1:" + path1);
                Log.d("Alex", "queryString:" + queryString);
            }
        }
    }

這樣寫bean的字段,字段是new_p也能解析
@SerializedName("new_p")
private String psw;

ViewPager越界
ViewPager的子控件個數(getChildCount())最多爲3(在destroyItem()中調用了removeView()),若是要獲取ViewPager某個子頁面,千萬不能使用getChildAt(position),會形成數組越界。能夠經過這樣來獲取。

@Override
public Object instantiateItem(ViewGroup container, int position) {
    view.setTag(DEFAULT_TAG + position);
    container.addView(view);
    return view;
}

View child = mViewPager.findViewWithTag(DEFAULT_TAG + position));
相關文章
相關標籤/搜索