Android开发过程中遇到的问题(长期补充)

本文汇总了Android开发中常见的实用技巧,包括获取屏幕尺寸、PopupWindow布局调整、判断第三方应用安装状态、处理拍照回调数据、图片操作及权限管理等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

长期补充,我只是大自然的搬运工

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.youkuaiyun.com/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);
<think>好的,用户现在问的是在Android Studio中使用KTS还是Groovy。之前他们问过kts和grooye,我纠正过Grooye可能是Groovy的拼写错误,现在他们正确使用了Groovy,所以这次的问题更明确了。 首先,我需要确定用户的使用场景。他们可能是在配置Gradle构建脚本时遇到了选择困难,或者刚接触Android开发,对这两种DSL不太了解。用户可能是开发者,需要了解两者的区别和优劣势,以便做出合适的选择。 接下来,我要比较KTS(Kotlin DSL)和Groovy的优缺点。需要涵盖语法差异、性能、IDE支持、社区趋势等方面。还要提到迁移成本,因为用户可能已经在用Groovy,考虑是否值得切换到KTS。 用户可能关心的问题包括:哪种更容易维护,未来是否会被淘汰,有没有兼容性问题,学习曲线如何。需要指出KTS是官方推荐的方向,但Groovy目前还是有很多项目在用,特别是旧项目。 另外,要给出实际建议,比如新项目推荐KTS,旧项目逐步迁移,以及如何开始使用KTS,比如转换build.gradle到.build.gradle.kts,使用IDE的转换工具,注意语法差异,分阶段迁移。 还要注意之前用户提到的系统级指令,确保数学公式用$...$,独立公式用$$...$$,并且用中文回答,结构清晰。需要检查是否有遗漏的要点,比如是否需要示例代码,但用户这次没有明确要求,可能不需要,但可以提到在build.gradle.kts中的基本写法。 最后,总结两者的对比,强调根据项目情况选择,并指出未来的趋势是KTS,但Groovy仍会存在一段时间。确保回答真实可靠,参考官方文档和社区实践。</think>以下是针对 **Android Studio 中使用 KTS(Kotlin DSL)还是 Groovy** 的详细对比与建议: --- ### 一、**核心区别** | **特性** | **Groovy (传统 DSL)** | **KTS (Kotlin DSL)** | |------------------|-----------------------------------------------|-----------------------------------------------| | **语法** | 动态类型,宽松语法(允许省略括号、分号等) | 静态类型,严格语法(需遵循 Kotlin 语法规则) | | **类型安全** | 弱类型,依赖运行时检查,易隐藏错误 | 强类型,编译时检查,减少配置错误 | | **IDE 支持** | 自动补全和导航较弱 | 强大的自动补全、类型推导和代码导航 | | **性能** | 构建速度较快(历史优化成熟) | 早期较慢,随版本优化逐渐接近 Groovy | | **可维护性** | 简单脚本易写,复杂逻辑可读性差 | 结构化更强,适合大型项目 | | **未来趋势** | 逐步被官方弱化,但仍有大量存量项目 | 官方推荐方向,新项目首选 | --- ### 二、**Groovy 的优势与劣势** #### **优势** 1. **上手简单**:语法灵活,适合快速编写简单脚本。 ```groovy android { compileSdkVersion 33 defaultConfig { minSdk 21 } } ``` 2. **成熟稳定**:长期作为 Android 默认 DSL,社区资源丰富。 3. **构建速度**:历史优化更充分,大型项目可能略快于 KTS。 #### **劣势** 1. **类型不安全**:配置错误可能延迟到运行时才发现。 2. **维护成本高**:复杂逻辑可读性差,重构困难。 3. **IDE 支持弱**:代码提示和跳转功能有限。 --- ### 三、**KTS 的优势与劣势** #### **优势** 1. **类型安全**:编译时检查配置错误,减少构建失败。 ```kotlin android { compileSdk = 33 defaultConfig { minSdk = 21 } } ``` 2. **代码可维护性**:支持函数、类等结构化编程,适合复杂逻辑。 3. **IDE 友好**:Android Studio 对 KTS 的自动补全、重构支持更完善。 4. **与 Kotlin 生态统一**:可直接调用 Kotlin 代码或库。 #### **劣势** 1. **学习曲线**:需熟悉 Kotlin 语法(尤其对 Java 开发者)。 2. **早期性能问题**:过去构建速度较慢,但 AGP 7.0+ 已显著优化。 3. **兼容性**:某些旧插件可能未适配 KTS。 --- ### 四、**实际建议** #### 1. **新项目** - **优先选择 KTS**:官方推荐方向,长期维护成本更低,尤其适合中大型项目。 - 示例:直接创建 `.gradle.kts` 文件,使用 Kotlin 语法编写配置。 #### 2. **旧项目** - **逐步迁移**:从 `settings.gradle` 和根 `build.gradle` 开始,分模块迁移。 - 工具支持:Android Studio 提供 **Groovy → KTS 自动转换工具**(右键点击文件选择转换)。 #### 3. **关键注意事项** - **语法差异**: - Groovy:`compileSdkVersion 33` - KTS:`compileSdk = 33`(注意 `=` 赋值和类型推断) - 字符串处理:KTS 中需显式使用 `"${variable}"` 模板。 - **插件兼容性**:确认第三方插件是否支持 KTS(查看文档或 Issue 讨论)。 - **性能调优**:启用配置缓存(`--configuration-cache`)提升构建速度。 --- ### 五、**迁移步骤示例** 1. **重命名文件**:`build.gradle` → `build.gradle.kts`。 2. **修正语法**: ```kotlin // Groovy implementation 'com.android.support:appcompat-v7:28.0.0' // KTS implementation("com.android.support:appcompat-v7:28.0.0") // 注意括号和引号 ``` 3. **处理变量**:将 `ext` 全局变量改为 `extra.set("versionCode", 1)` 或使用 `gradle.properties`。 --- ### 六、**总结** - **未来趋势**:Google 正推动 KTS 成为 Android 生态标准,**推荐优先使用**。 - **过渡期策略**:Groovy 短期内不会消失,但新特性(如 Version Catalogs)更倾向 KTS。 - **决策依据**:根据团队技术栈(是否熟悉 Kotlin)和项目规模选择。 如果需要更具体的迁移案例或问题排查,可以进一步补充上下文! 🚀
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值