记录几个碰到的感觉网上没有找到很好解决办法的问题。
一、通过ContentProvider数据集获取歌曲数据
获取数据时,我们需要在 m a n i f e s t manifest manifest文件中添加权限,如果按照第十章ppt上的方法使用 A c t i v i t y R e s u l t L a u n c h e r ActivityResultLauncher ActivityResultLauncher来获取歌曲,初始化你的 L i s t List List时,顺序会和写的顺序不一样。
举个例子
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mActivity = this;
Log.d("myflag", "0");
permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE);
Log.d("myflag", "2");
Init_View();
Init_RecyclerView();
}
private final ActivityResultLauncher permissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if (result) {
Log.d("myflag","1");
Load_Local_Music();
}
}
}
);
先看这两个图,你可能会认为 L o g . d Log.d Log.d中数字输出的顺序是012,但是实际上输出的顺序是021。这是为什么呢?主观上我们可能把 L a u n c h e r Launcher Launcher当成一个普通的函数了,但实际上他会在 O n C r e a t e OnCreate OnCreate函数执行结束后这个回调函数才会被调用。这会导致什么问题呢?本来我们想的是先初始化了 L i s t List List后再去用这个 L i s t List List初始化 R e c y c l e r V i e w RecyclerView RecyclerView的 A d a p t e r Adapter Adapter,但是实际上我们在初始化 A d a p t e r Adapter Adapter时 L i s t List List并没有被初始化,就会出问题。
解决这个问题的办法有两个:
①把初始化 R e c y c l e r V i e w RecyclerView RecyclerView的部分也写到启动器的回调函数里
②把初始化 L i s t List List的函数不放在启动器的回调函数里,而是直接放在 o n C r e a t e onCreate onCreate里,但是这样要确保权限已经申请好了(提前在外面开好或者用一次launch让他弹出来给你点)
二、关于用来初始化 A d a p t e r Adapter Adapter的 L i s t List List
我们可以看到第十章的PPT里获取音乐列表的 L i s t List List是让 L i s t List List等于了 g e t M u s i c L i s t getMusicList getMusicList的返回值,这样得到的 L i s t List List是没问题的,有的人想这样让 L i s t List List等于之后调用 a d a p t e r . n o t i f y D a t a S e t C h a n g e d ( ) adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()之后 R e c y c l e r V i e w RecyclerView RecyclerView就正常了。很可惜,这样的想法是错误的。按我的理解, A d a p t e r Adapter Adapter里的 L i s t List List类似指针,还是指向原来的那个 L i s t List List,而你让主函数里的 L i s t List List直接等于另一个 L i s t List List的话,修改了指针,但是 A d a p t e r Adapter Adapter里 L i s t List List的指针并没有被修改。
如果上面理解不了的话,就记住 A d a p t e r Adapter Adapter初始化用的 L i s t List List在用来初始化 A d p a t e r Adpater Adpater之后就不要用 = = =来更改了,只能在原来的 L i s t List List的基础上增减。
解决方法:
①先初始化 L i s t List List,在初始化 A d a p t e r Adapter Adapter
②不用=来修改 L i s t List List,而是一个一个的 a d d add add,修改完后 n o t i f y n o t i f y D a t a S e t C h a n g e d ( ) notifynotifyDataSetChanged() notifynotifyDataSetChanged()一下
三、自定义 n o t i f i c a t i o n notification notification视图和点击事件
这部分网上讲的要么不清楚,要么版本和现在差太远了,所以在这里从头到尾讲一下。
我们先自定义一个 L i n e a r L a y o u t LinearLayout LinearLayout的视图,来作为我们填充到通知里的布局,需要注意的是布局的宽度不要超过256dp,否则通知栏里装不下,会显示不全。
视图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
他们的ID
然后我们需要用到一个新东西—— R e m o t e V i e w RemoteView RemoteView
直接用代码来演示好了
remoteViews = new RemoteViews(getPackageName(),R.layout.notification_view);
// 当成默认格式就行 你刚刚自定义的视图
//将图片绑定到视图里的ImageView上,第一个参数是控件的ID,第二个参数是你自己的图片
remoteViews.setImageViewResource(R.id.not_previous, R.drawable.previous);
remoteViews.setImageViewResource(R.id.not_play, R.drawable.play);
remoteViews.setImageViewResource(R.id.not_pause, R.drawable.pause);
remoteViews.setImageViewResource(R.id.not_next, R.drawable.next);
RemoteView中,如果你的 I m a g e V i e w ImageView ImageView没有绑定,那么它对应的位子显示的会是空白,而如果是 T e x t V i e w TextView TextView,他则会显示你在视图中写的东西。
接下来把他设置到 N o t i f i c a t i o n Notification Notification中
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
channel = new NotificationChannel(
"MyChannel",
"Channel_Name",
NotificationManager.IMPORTANCE_HIGH
);
manager.createNotificationChannel(channel);
builder = new Notification.Builder(this, "MyChannel")
// .setCustomContentView(remoteViews)
.setCustomBigContentView(remoteViews)
// .setCustomHeadsUpContentView(remoteViews)
.setSmallIcon(R.drawable.twitter);
notification = builder.build();
manager.notify(123, notification);
只需要将 r e m o t e V i e w s remoteViews remoteViews设置到 b u i l d e r builder builder中,这里有三种设置方式:
①setCustomContentView 通知中布局高度固定为64dp,如果视图太大了会显示不全。
②setCustomBigContentView 可以将通知下拉,展开你的全部视图
③setCustomBigContentView 将小图标放到了最前面
可以把它们都试一次,就知道具体的效果会是什么了。
###builder中必须设置SmallIcon,否则通知将创建不成功,其他的图标可以不用管了,因为你有了自定义的布局。
接下来是重点
如何设置点击事件
这里用到的是 B r o a d C a s t BroadCast BroadCast来实现的
还是来看例子
Intent intent_previous = new Intent(this,NotificationClickReceiver.class);
//Intent intent_previous = new Intent();
intent_previous.setAction("previous");
PendingIntent pendingIntent_previous = PendingIntent.getBroadcast(this, 0, intent_previous, PendingIntent.FLAG_IMMUTABLE);
remoteViews.setOnClickPendingIntent(R.id.not_previous, pendingIntent_previous);
然后来讲解一下,这里我们先创建了一个从 t h i s this this到 N o t i f i c a t i o n C l i c k R e c e i v e r NotificationClickReceiver NotificationClickReceiver的 I n t e n t Intent Intent,然后我们给他设置了标识 A c t i o n Action Action,再用这个 I n t e n t Intent Intent创建了一个 P e n d i n g I n e t e n t PendingInetent PendingInetent,最后将这个 P e n d i n g I n t e n t PendingIntent PendingIntent和 r e m o t e V i e w s remoteViews remoteViews里的控件绑定起来。
###记得静态或者动态注册一下 B r o a d C a s t BroadCast BroadCast
*如何理解 P e n d i n g I n t e n t PendingIntent PendingIntent?
P e n d i n g I n t e n t PendingIntent PendingIntent可以就看做是一个满足一定条件就执行的事件
它有三种创建方式:
①getActivity(Context context, int requestCode, Intent intent, int flags)
②getService(Context context, int requestCode, Intent intent, int flags)
③getBroadcast(Context context, int requestCode, Intent intent, int flags)
这三种方式可以看成被执行的事件,第一个就是满足了条件就跳转到一个 A c t i v i t y Activity Activity,第二个是启动一个 S e r v i c e Service Service,第三个是发送一个广播。
r e q u e s t C o d e requestCode requestCode可以随便填,这里不太用得到它, i n t e n t intent intent可以理解为起点和终点,看向上面的代码,第一行写的可以理解为起点是当前的地方,终点是 N o t i f i c a t i o n C l i c k R e c e i v e r NotificationClickReceiver NotificationClickReceiver,我们发的通知只有在这个receiver里才会被处理,而第二行注释掉的就没有这个限制,所有的 R e c e i v e r Receiver Receiver都可以收到他的广播。
f l a g s flags flags有六种值,感兴趣的可以去查一下,这里用的 P e n d i n g I n t e n t . F L A G — I M M U T A B L E PendingIntent.FLAG_—IMMUTABLE PendingIntent.FLAG—IMMUTABLE标记构建无法被修改的 P e n d i n g I n t e n t PendingIntent PendingIntent,常用于由于明确知道未来需要进行的操作。这里如果使用的Flag错误会导致绑定出问题。
那么这里的一定条件是什么呢?答案是我们绑定到控件上的onClick。
最后我们在接收器中判断 a c t i o n action action,然后来写点击事件就完成了。
public class NotificationClickReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
MainActivity mainActivity=MainActivity.getInstace();
String action = intent.getAction();
if(action.equals("play")){
*****
}
if(action.equals("pause")){
*****
}
if(action.equals("next")){
*****
}
if(action.equals("previous")){
*****
}
}
}
最后建议各位去网上找关于 I n t e n t Intent Intent、 S e r v i c e Service Service和 B r o a d C a s t BroadCast BroadCast的内容看一下,那PPT上的东西属实是太少了。
T i p s Tips Tips
设置 R e c y c l e r V i e w RecyclerView RecyclerView各个 i t e m item item间距:链接