Android音乐App桌面图标制作以及启动页面开发( 简易音乐 一 )
关于
其实一直想做一款属于自己的音乐app,然后给自己的好朋友用。但是因为种种原因和事情一直拖了好久没有付诸行动。前两天终于下定决心要在从新开发一个app。而且开发的过程也基本都会以专栏的形式记录下来,包括源码啥的。因为会很认真的坐下来,包括UI细节以及优化等等。
开发工具Android Studio 3.5
版本 Android androidx 9.0
效果
首先上一下启动页面以及桌面图标logo的gif:
第一步
关于桌面图标的实现,参考我的这篇文章 Android 实现App修改应用图标 。这里需要注意的一点是在最后一步,也就是这里选择circle,则显示的是圆形图标,选择square为正方形图标:
第二步
先看一下项目结构:
其中splashActivity是我们的启动页,PermissionHelper是我们的权限申请帮助类。
第一步 修改SplashAcitivity的布局页面
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/red_trans"
tools:context=".jy.personal.music.activity.SplashActivity">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@drawable/launcher_back" />
<!--图标资源会在博客最后放到网盘中-->
<TextView
android:layout_width="wrap_content"
android:layout_below="@+id/image"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/app_name"
android:layout_marginTop="16dp"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="#2c2c2c"/>
</RelativeLayout>
第二步 修改androidmanifest.xml
添加要用到的部分权限(后面可能还会新增权限申请)
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <!--允许安装和卸载文件系统权限-->
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!--申请电源锁权限-->
<uses-permission android:name = "android.permission.DISABLE_KEYGUARD"/> <!--解锁屏幕需要的权限-->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!--全局对话弹出框,可能会有部分机型有问题,暂不考虑-->
修改splashactivity为启动页:
<activity android:name=".jy.personal.music.activity.SplashActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
第三步 权限申请帮助类
单独的权限申请方法如下:
if (Build.VERSION.SDK_INT>22){
if (ContextCompat.checkSelfPermission(QueryPackingBarrelActivity.this,
android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
//先判断有没有权限 ,没有就在这里进行权限的申请
ActivityCompat.requestPermissions(QueryPackingBarrelActivity.this,
new String[]{android.Manifest.permission.CAMERA},991);
}else {
//说明已经获取到摄像头权限了 想干嘛干嘛
}
}else {
//这个说明系统版本在6.0之下,不需要动态获取权限。
//说明已经获取到摄像头权限了 想干嘛干嘛
}
/**
* 高版本动态获取摄像头权限
* @param requestCode 请求码
* @param permissions 请求的权限
* @param grantResults 返回的权限数据
*/
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) {
switch (requestCode){
case 991:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
//这里已经获取到了摄像头的权限,想干嘛干嘛了可以
}else {
//这里是拒绝给APP摄像头权限,给个提示什么的说明一下都可以。
Toast.makeText(QueryPackingBarrelActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
下面是封装的:
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.util.Log;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
/**
* Android 6.0 上权限分为<b>正常</b>和<b>危险</b>级别
* <ul>
* <li>正常级别权限:开发者仅仅需要在AndroidManifext.xml上声明,那么应用就会被允许拥有该权限,如:android.permission.INTERNET</li>
* <li>危险级别权限:开发者需要在AndroidManifext.xml上声明,并且在运行时进行申请,而且用户允许了,应用才会被允许拥有该权限,如:android.permission.WRITE_EXTERNAL_STORAGE</li>
* </ul>
* 有米的以下权限需要在Android6.0上被允许,有米广告sdk才能正常工作,开发者需要在调用有米的任何代码之前,提前让用户允许权限
* <ul>
* <li>必须申请的权限
* <ul>
* <li>android.permission.READ_PHONE_STATE</li>
* <li>android.permission.WRITE_EXTERNAL_STORAGE</li>
* </ul>
* </li>
* <li>可选申请的权限
* <ul>
* <li>android.permission.ACCESS_FINE_LOCATION</li>
* </ul>
* </li>
* </ul>
* <p>Android 6.0 权限申请助手</p>
* Created by Alian on 16-1-12.
*/
public class PermissionHelper {
private static final String TAG = "PermissionHelper";
/**
* 小tips:这里的int数值不能太大,否则不会弹出请求权限提示,测试的时候,改到1000就不会弹出请求了
*/
private final static int READ_PHONE_STATE_CODE = 101;
private final static int WRITE_EXTERNAL_STORAGE_CODE = 102;
private final static int REQUEST_OPEN_APPLICATION_SETTINGS_CODE = 12345;
/**
* 有米 Android SDK 所需要向用户申请的权限列表
*/
private PermissionModel[] mPermissionModels = new PermissionModel[] {
new PermissionModel("电话", Manifest.permission.READ_PHONE_STATE, "我们需要读取手机信息的权限来标识您的身份", READ_PHONE_STATE_CODE),
new PermissionModel("存储空间", Manifest.permission.WRITE_EXTERNAL_STORAGE, "我们需要您允许我们读写你的存储卡,以方便我们临时保存一些数据",
WRITE_EXTERNAL_STORAGE_CODE)
};
private Activity mActivity;
private OnApplyPermissionListener mOnApplyPermissionListener;
public PermissionHelper(Activity activity) {
mActivity = activity;
}
public void setOnApplyPermissionListener(OnApplyPermissionListener onApplyPermissionListener) {
mOnApplyPermissionListener = onApplyPermissionListener;
}
/**
* 这里我们演示如何在Android 6.0+上运行时申请权限
*/
public void applyPermissions() {
try {
for (final PermissionModel model : mPermissionModels) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(mActivity, model.permission)) {
ActivityCompat.requestPermissions(mActivity, new String[] { model.permission }, model.requestCode);
return;
}
}
if (mOnApplyPermissionListener != null) {
mOnApplyPermissionListener.onAfterApplyAllPermission();
}
} catch (Throwable e) {
Log.e(TAG, "", e);
}
}
/**
* 对应Activity的 {@code onRequestPermissionsResult(...)} 方法
*
* @param requestCode
* @param permissions
* @param grantResults
*/
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case READ_PHONE_STATE_CODE:
case WRITE_EXTERNAL_STORAGE_CODE:
// 如果用户不允许,我们视情况发起二次请求或者引导用户到应用页面手动打开
if (PackageManager.PERMISSION_GRANTED != grantResults[0]) {
// 二次请求,表现为:以前请求过这个权限,但是用户拒接了
// 在二次请求的时候,会有一个“不再提示的”checkbox
// 因此这里需要给用户解释一下我们为什么需要这个权限,否则用户可能会永久不在激活这个申请
// 方便用户理解我们为什么需要这个权限
if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, permissions[0])) {
AlertDialog.Builder builder =
new AlertDialog.Builder(mActivity).setTitle("权限申请").setMessage(findPermissionExplain(permissions[0]))
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
applyPermissions();
}
});
builder.setCancelable(false);
builder.show();
}
// 到这里就表示已经是第3+次请求,而且此时用户已经永久拒绝了,这个时候,我们引导用户到应用权限页面,让用户自己手动打开
else {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity).setTitle("权限申请")
.setMessage("请在打开的窗口的权限中开启" + findPermissionName(permissions[0]) + "权限,以正常使用本应用")
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
openApplicationSettings(REQUEST_OPEN_APPLICATION_SETTINGS_CODE);
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mActivity.finish();
}
});
builder.setCancelable(false);
builder.show();
}
return;
}
// 到这里就表示用户允许了本次请求,我们继续检查是否还有待申请的权限没有申请
if (isAllRequestedPermissionGranted()) {
if (mOnApplyPermissionListener != null) {
mOnApplyPermissionListener.onAfterApplyAllPermission();
}
} else {
applyPermissions();
}
break;
}
}
/**
* 对应Activity的 {@code onActivityResult(...)} 方法
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_OPEN_APPLICATION_SETTINGS_CODE:
if (isAllRequestedPermissionGranted()) {
if (mOnApplyPermissionListener != null) {
mOnApplyPermissionListener.onAfterApplyAllPermission();
}
} else {
mActivity.finish();
}
break;
}
}
/**
* 判断是否所有的权限都被授权了
*
* @return
*/
public boolean isAllRequestedPermissionGranted() {
for (PermissionModel model : mPermissionModels) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(mActivity, model.permission)) {
return false;
}
}
return true;
}
/**
* 打开应用设置界面
*
* @param requestCode 请求码
*
* @return
*/
private boolean openApplicationSettings(int requestCode) {
try {
Intent intent =
new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + mActivity.getPackageName()));
intent.addCategory(Intent.CATEGORY_DEFAULT);
// Android L 之后Activity的启动模式发生了一些变化
// 如果用了下面的 Intent.FLAG_ACTIVITY_NEW_TASK ,并且是 startActivityForResult
// 那么会在打开新的activity的时候就会立即回调 onActivityResult
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mActivity.startActivityForResult(intent, requestCode);
return true;
} catch (Throwable e) {
Log.e(TAG, "", e);
}
return false;
}
/**
* 查找申请权限的解释短语
*
* @param permission 权限
*
* @return
*/
private String findPermissionExplain(String permission) {
if (mPermissionModels != null) {
for (PermissionModel model : mPermissionModels) {
if (model != null && model.permission != null && model.permission.equals(permission)) {
return model.explain;
}
}
}
return null;
}
/**
* 查找申请权限的名称
*
* @param permission 权限
*
* @return
*/
private String findPermissionName(String permission) {
if (mPermissionModels != null) {
for (PermissionModel model : mPermissionModels) {
if (model != null && model.permission != null && model.permission.equals(permission)) {
return model.name;
}
}
}
return null;
}
private static class PermissionModel {
/**
* 权限名称
*/
public String name;
/**
* 请求的权限
*/
public String permission;
/**
* 解析为什么请求这个权限
*/
public String explain;
/**
* 请求代码
*/
public int requestCode;
public PermissionModel(String name, String permission, String explain, int requestCode) {
this.name = name;
this.permission = permission;
this.explain = explain;
this.requestCode = requestCode;
}
}
/**
* 权限申请事件监听
*/
public interface OnApplyPermissionListener {
/**
* 申请所有权限之后的逻辑
*/
void onAfterApplyAllPermission();
}
}
第四步 修改splashAcitivity.java
/**
* 启动页
* @version V1.0.0
* @author Tobey_R1
* @since 2020年8月28日
*/
public class SplashActivity extends AppCompatActivity {
private Context mContext;
private PermissionHelper mPermissionHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//隐藏状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
//移除标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_splash);
//党系统版本6.0+,需要权限申请
mPermissionHelper = new PermissionHelper(this);
mPermissionHelper.setOnApplyPermissionListener(new PermissionHelper.OnApplyPermissionListener() {
@Override
public void onAfterApplyAllPermission() {
runApp();
}
});
if (Build.VERSION.SDK_INT<23){
runApp();
}else {
//如果权限全部申请了,直接运行
if (mPermissionHelper.isAllRequestedPermissionGranted()){
runApp();
}else {
//如果还有权限未申请,且版本大于23,执行申请权限逻辑
mPermissionHelper.applyPermissions();
}
}
}
/**
* 高版本动态获取权限
* @param requestCode 请求码
* @param permissions 请求的权限
* @param grantResults 返回的权限数据
* */
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
mPermissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mPermissionHelper.onActivityResult(requestCode, resultCode, data);
}
private void runApp() {
//设置开屏
setupSplashAd();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
private void setupSplashAd() {
//创建开屏容器 延迟显示2s
Timer tmpTimer = new Timer();
tmpTimer.schedule(new TimerTask() {
@Override
public void run() {
//根据有无缓存判断
Intent intent = new Intent();
intent.setClass(SplashActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
startActivity(intent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
finish();
//这里finish();未添加的话在现有代码情况下运行测试会有bug。
}
}, 2000);
}
}
文章的最后放一下图标资源链接:
https://pan.baidu.com/s/1NXGB75ftAj4WSh3UUeD_yw 提取码 tobe。