天天看點

Android開發過程中遇到的問題(長期補充)

長期補充,我隻是大自然的搬運工

1.擷取手機的寬度和高度

WindowManager wm = this.getWindowManager();
int width = wm.getDefaultDisplay().getWidth();
int height = wm.getDefaultDisplay().getHeight();
           

2.popupWindow布局居中顯示

popup.setHeight(LayoutParams.WRAP_CONTENT);  
popup.setWidth(width*3/4); //為手機的寬度(上面的width)
popup.showAtLocation(listView, Gravity.CENTER,0,0);//s
           

3.判斷手機是否安裝某第三方app

項目中遇到,需要判斷支付寶是否安裝,彈出提示。像判斷微信是否安裝,自己的jar包中會有isWXAppInstalled的方法。但是支付寶貌似沒找到

想要判斷是否安裝第三方app,就必須知道包名,包名的話,隻能去反編譯了。從AndroidManifest 去找到對應的package屬性。

開發中,如果突然想改項目的包名,在Android Tools 中 rename Application Package就可以了

好了,言歸正傳,有了包名,隻要周遊這個手機中所有的應用的包名就可以了,網上找了一個可用的方法,我覺得寫的還是挺詳細的

/** 
 * 檢查手機上是否安裝了指定的軟體 
 * @param context 
 * @param packageName:應用包名 如 com.example.test
 * @return 
 */  
private boolean isAvilible(Context context, String packageName){   
    //擷取packagemanager   
    final PackageManager packageManager = context.getPackageManager();  
  //擷取所有已安裝程式的包資訊   
    List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);  
  //用于存儲所有已安裝程式的包名   
    List<String> packageNames = new ArrayList<String>();  
    //從pinfo中将包名字逐一取出,壓入pName list中   
    if(packageInfos != null){   
        for(int i = 0; i < packageInfos.size(); i++){   
            String packName = packageInfos.get(i).packageName;   
            packageNames.add(packName);   
        }   
    }   
  //判斷packageNames中是否有目标程式的包名,有TRUE,沒有FALSE   
    return packageNames.contains(packageName);  
}   
           

4.關于拍照的回調傳回的資料的問題

在onActivityResult中,從傳回的Intent中,獲得的Uri uri = data.getData();uri在4.3—->4.4系統的時候産生了不同在4.4一下,可以直接使用,但是在4.4以後,傳回的是content://。。。。,會出現無法加載此圖檔。

private void handleData(Intent data) {
    Uri uri = data.getData();
    // 4.4以後新特征
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        String url = getPath(PersonalDataActivity.this, uri);
        uri = Uri.fromFile(new File(url));
        handleData(uri);

    } else {
        // 4.4以下
        handleData(uri);
    }
}
           

通過判斷當然系統的版本進行不同的處理,主要是getPath這個方法。網上也有這個方法,測試可用

// 4.4新特征
public String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/"
                        + split[1];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"),
                    Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] { split[1] };

            return getDataColumn(context, contentUri, selection,
                    selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 * 
 * @param context
 *            The context.
 * @param uri
 *            The Uri to query.
 * @param selection
 *            (Optional) Filter used in the query.
 * @param selectionArgs
 *            (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri,
        String selection, String[] selectionArgs) {

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = { column };

    try {
        cursor = context.getContentResolver().query(uri, projection,
                selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri
            .getAuthority());
}

/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri
            .getAuthority());
}
           

開發的時候需要把自己開發時候的包改為4.4.因為DocumentsContract 是在4.4包中獨有的類。

4.3的開發包是找不到這個類的,會報紅。

5.把網絡圖檔轉化為存在本地

先将流轉化為bitmap對象

map = BitmapFactory.decodeStream(inStream);
           

然後再講bitmap存在本地。

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
map .compress(Bitmap.CompressFormat.JPEG, 80, bos);
           

其中的file可以調用這個File(String dirPath, String name) ,目錄加名字來構造

6.本地儲存圖檔之後需要重新整理相冊

//相冊掃描圖檔
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(new File(mFilePath));
intent.setData(uri);
sendBroadcast(intent);
           

7.需要判斷第三方應用是否安裝(代碼在上)

如果未安裝,跳轉下載下傳,調到浏覽器下載下傳

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("下載下傳網址"));
mContext.startActivity(intent);
           

如果安裝了,直接打開app

PackageManager packageManager = mContext.getPackageManager();   
Intent intent1 = packageManager.getLaunchIntentForPackage("包名");  
mContext.startActivity(intent1);  
           

8.撥打電話

Intent  intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+ "電話号碼"));
startActivity(intent);
           

9.跳轉到打電話的頁面

Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + telephone));
context.startActivity(intent);
           

10.複制到剪切闆

public static void copy2Clipboard(Context context,String msg){
    ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
    // 将文本内容放到系統剪貼闆裡。
    ClipData clip = ClipData.newPlainText("simple text", msg);

    // Set the clipboard's primary clip.
    cm.setPrimaryClip(clip);
    Toast.makeText(context, "複制成功", Toast.LENGTH_LONG).show();
}
           

11.有EditText時候進入界面預設彈出輸入框

防止有EditText時候進入界面直接彈出輸入框,不友好

可以在EditText前面放置一個看不到的LinearLayout

<LinearLayout
    android:focusable="true" 
    android:focusableInTouchMode="true"
    android:layout_width="0px" 
    android:layout_height="0px"/>
           

或者在他的父布局中加入

android:focusable="true" 
    android:focusableInTouchMode="true"
           

即可

12.關于WebView的使用用到的問題

1.加載完顯示空白頁:

@Override public boolean shouldOverrideUrlLoading(WebView view, String url) {  
    CommUtil.logD(TAG, "shouldOverrideUrlLoading, url:" + url);  
    return false;  
}  
           

傳回false 就好了

2.webview 中加載頁面的對話框

如果需要彈出網頁的對話框之類的,需要設定如下

mWebView.setWebChromeClient(new WebChromeClient(){
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            return super.onJsAlert(view, url, message, result);
        }
    });
           

13.android 6.0的權限的問題

android 6.0的手機就算manifest檔案中設定了存儲權限,app運作時候還是需要手動申請,否則在app裡面建立檔案夾會失敗。

需要 調用 showPremissDialogIfNeccessary 就好了

private static final int REQUEST_CODE_FOR_WRITE_PERMISSON = 1;
public void showPremissDialogIfNeccessary(){
    //6.0 權限的手機需要打開權限
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if(hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED){
            requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_CODE_FOR_WRITE_PERMISSON);
            return;
        }
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode == REQUEST_CODE_FOR_WRITE_PERMISSON){
        if(permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            //同意就情況
        }else{
            Toast.makeText(mContext, R.string.request_write_pression_failed, Toast.LENGTH_SHORT).show();
        }
    }
}   
           

由于上述的方法中是在sdk 為 6.0 的方法存在的,這裡我們可以使用v4擴充包來幫助我們實作權限判斷,

ContextCompat.checkSelfPermission()

ActivityCompat.requestPermissions()

ActivityCompat.OnRequestPermissionsResultCallback

ActivityCompat.shouldShowRequestPermissionRationale()

首先這個activity 要實作 ActivityCompat.OnRequestPermissionsResultCallback方法,這樣就必須重寫onRequestPermissionsResult方法。

private static final int REQUEST_CODE_FOR_WRITE_PERMISSON = 1;
public void showPremissDialogIfNeccessary(){
    //大于6.0 權限的手機需要打開權限
    if(Build.VERSION.SDK_INT >= 23){
        int hasWriteContactsPermission = ContextCompat.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if(hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_CODE_FOR_WRITE_PERMISSON);
            return;
        }
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if(requestCode == REQUEST_CODE_FOR_WRITE_PERMISSON){
        if(permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[0] == PackageManager.PERMISSION_GRANTED){
            //同意
        }else{
            Toast.makeText(mContext, "擷取存儲權限失敗"), Toast.LENGTH_SHORT).show();
        }
    }
}
           

具體的可以參考這篇部落格

http://blog.csdn.net/kong_gu_you_lan/article/details/52488097

14.Listview的item最後一個不需要實作divider的樣式

關于Listview 的divider的設定問題,如果資料item最後一個不需要現實divider的樣式(分割線),需要将Listview 設定成wrap_content就好了(recyclerview不知道,有興趣的同學可以試一下)

15.删除圖檔後,從相冊選擇還是存在縮略圖

安卓圖庫都是在資料庫備份縮略圖的,删除原圖後,你需要去更新mediaprovider。在直行了File的delete方法後,其實縮略圖還在,現在要做的就是需要去更新資料庫删除縮略圖。

方法如下:

1.發廣播的方式

public void scanFileAsync(Context ctx, String filePath) { 
        Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 
        scanIntent.setData(Uri.fromFile(new File(filePath))); 
        ctx.sendBroadcast(scanIntent); 
} 
           

2.更新資料庫

getcontentresolver.delete(Media.EXTERNAL_CONTENT_URI, Media.DATA + "=?",path);