Android知识点<10>Android View - RomoteViews , SurfaceView,RecycleView,ListView

本文介绍了Android中Activity-Window-View的区别,并重点讲解了RemoteViews、RecyclerView和ListView的使用及优化。针对ListView的图片加载错乱问题,提出了解决方案。此外,对比了RecyclerView与ListView在缓存机制和局部刷新上的差异,揭示了RecyclerView在性能优化上的优势。

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

问题 : Activity-Window-View三者的差别

Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图) LayoutInflater像剪刀,Xml配置像窗花图纸。

  1. 在Activity中调用attach,创建了一个Window
  2. 创建的window是其子类PhoneWindow,在attach中创建PhoneWindow
  3. 在Activity中调用setContentView(R.layout.xxx)
  4. 其中实际上是调用的getWindow().setContentView()
  5. 调用PhoneWindow中的setContentView方法
  6. 创建ParentView:
作为ViewGroup的子类,实际是创建的DecorView(作为FramLayout的子类)
  7. 将指定的R.layout.xxx进行填充
通过布局填充器进行填充【其中的parent指的就是DecorView】
  8. 调用到ViewGroup
  9. 调用ViewGroup的removeAllView(),先将所有的view移除掉
  10. 添加新的view:addView()

1.RomoteViews


1.1  Android为了能让进程A显示进程B的View,设计了RmoteView。主要应用于两个场景 : 通知栏通知,桌面小程序

1.2  通知栏

RemoteViews remoteViews = new RemoteViews(getPackageName(),R.layout.layout_notification);

remoteViews.setTextViewText(R.id.msg, "chapter_5: " + sId);

remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1);

PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(this,0new Intent(this, DemoActivity_2.class),                                                                                                                 PendingIntent.FLAG_UPDATE_CURRENT);

remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent);

1.3  桌面小部件
桌面小部件通过AppWidgetProvider来实现的,它的本质是一个广播。
AppWidgetProvider提供了几个主要的方法, onUpdate, onEnable, onDisable, onDeleted 以及 onReceive。
其中onReceive会根据广播的Action响应, 然后再调用其它方法。
当桌面小部件接收到用户的交互信息,则会通过onReceive传递, 用户通过重写onReceive方法,并判断intent.getAction()是否需要做相应的处理。 如果是更新界面,就需要通过RemoveView实现:

@Override
    public void onReceive(final Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.i(TAG, "onReceive : action = " + intent.getAction());

        // 这里判断是自己的action,做自己的事情,比如小工具被点击了要干啥,这里是做一个动画效果
        if (intent.getAction().equals(CLICK_ACTION)) {
            Toast.makeText(context, "clicked it", Toast.LENGTH_SHORT).show();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcbBitmap = BitmapFactory.decodeResource(
                            context.getResources(), R.drawable.icon1);
                    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                    for (int i = 0; i < 37; i++) {
                        float degree = (i * 10) % 360;
                        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
                        remoteViews.setImageViewBitmap(R.id.imageView1,rotateBitmap(context, srcbBitmap, degree));
                        Intent intentClick = new Intent();
                        intentClick.setAction(CLICK_ACTION);
                        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intentClick, 0);
                        remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(context, MyAppWidgetProvider.class),remoteViews);
                        SystemClock.sleep(30);
                    }

                }
            }).start();
        }
    }

1.4 PendingIntent

PendingIntent表示一种处于Pending状态的意图, 即是一种特定,等待,即将发生的意思。

其它进程如果想要在你的app上做一些事情,如果还是传一个Intent,他们是没有执行权限的, 你必须给他们传PendingIntent, 他们才可以执行,因为PendingIntent包含了执行的权限。
上面通知的那段代码, PendingIntent的应用场景,就是给Remoteview设定一个点击的行为,打开DemoActivity_2这个activity。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/gradient_background"> <!-- 标题栏 --> <RelativeLayout android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?android:attr/actionBarSize" android:background="#44bd32"> <Button android:id="@+id/fanHui" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:background="@android:color/transparent" android:foreground="?android:attr/selectableItemBackground" android:text="返回" android:textColor="@android:color/white" android:textSize="16sp" /> </RelativeLayout> <!-- 底部按钮 --> <LinearLayout android:id="@+id/footer_buttons" android:layout_width="match_parent" android:layout_height="1dp" android:layout_alignParentBottom="true" android:background="@android:color/white" android:orientation="horizontal"> <!-- <Button--> <!-- android:id="@+id/device_location"--> <!-- android:layout_width="match_parent"--> <!-- android:layout_height="match_parent"--> <!-- android:gravity="center"--> <!-- android:text="设备定位"--> <!-- android:textSize="19sp"--> <!-- android:background="#44bd32"--> <!-- android:textColor="@android:color/white" />--> </LinearLayout> <!-- 图片网格容器(添加滚动支持) --> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/footer_buttons" android:layout_below="@id/toolbar" android:background="@android:color/white" android:fillViewport="true" android:padding="0dp"> <GridLayout android:id="@+id/image_grid" android:layout_width="match_parent" android:layout_height="wrap_content" android:columnCount="2" android:padding="0dp" /> </ScrollView> </RelativeLayout> 修改图片网格容器<ScrollView android:layout_width="match_parent" android:layout_height="380dp" android:layout_above="@id/footer_buttons" android:layout_below="@id/toolbar" android:layout_marginBottom="298dp" android:background="@android:color/white" android:fillViewport="true" android:padding="0dp"> </ScrollView>成一个播放视频的模块,其它不变。
最新发布
06-27
<think>我们需要将原来的图片网格容器(ScrollView内包含GridLayout)替换为一个视频播放模块,同时保持其他布局不变。新的视频播放模块可以是一个VideoView或者SurfaceView等,这里我们使用VideoView作为示例。同时,由于原布局中底部有一个高度为1dp的LinearLayout(footer_buttons),我们需要确保视频模块在标题栏和底部按钮之间。修改步骤:1.移除原来的ScrollView及其内部的GridLayout。2.添加一个视频播放容器(例如VideoView)到相同的位置。注意:原布局中,ScrollView的高度设置为match_parent,并且通过layout_above和layout_below来定位。现在我们要设置视频模块的高度为380dp,并且下方有298dp的marginBottom(这个值可能是为了给底部按钮留空间,但原底部按钮高度只有1dp,这里可能是有意留出空间放置其他内容?)。不过根据要求,我们直接设置高度为380dp,marginBottom为298dp。但是,请注意,原底部按钮(footer_buttons)的高度只有1dp,而视频模块下方留出了298dp的空白,这可能不是我们想要的。根据要求,我们只改变图片网格容器为视频播放模块,其他不变。因此,我们保留底部按钮,但视频模块的高度和marginBottom按照要求设置。修改后的布局中,视频播放模块将占据高度380dp,并且距离底部按钮298dp(这个距离很大,可能是为了给其他内容留空间?)。如果不需要这么长的空白,可以调整marginBottom。我们使用VideoView来播放视频,并设置其id为@+id/video_view。修改后的布局代码:</think>### 解决方案:将图片网格容器替换为视频播放模块 我将把图片网格容器(ScrollView)替换为一个视频播放模块,同时保持其他布局不变。以下是修改后的布局代码: ```xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/gradient_background"> <!-- 标题栏 --> <RelativeLayout android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?android:attr/actionBarSize" android:background="#44bd32"> <Button android:id="@+id/fanHui" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:background="@android:color/transparent" android:foreground="?android:attr/selectableItemBackground" android:text="返回" android:textColor="@android:color/white" android:textSize="16sp" /> </RelativeLayout> <!-- 底部按钮 --> <LinearLayout android:id="@+id/footer_buttons" android:layout_width="match_parent" android:layout_height="1dp" android:layout_alignParentBottom="true" android:background="@android:color/white" android:orientation="horizontal"> <!-- 设备定位按钮可在此添加 --> </LinearLayout> <!-- 视频播放模块 --> <RelativeLayout android:layout_width="match_parent" android:layout_height="380dp" android:layout_above="@id/footer_buttons" android:layout_below="@id/toolbar" android:layout_marginBottom="298dp" android:background="@android:color/black" android:padding="0dp"> <!-- 视频播放器 --> <VideoView android:id="@+id/video_player" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" /> <!-- 播放控制条 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="#99000000" android:orientation="horizontal" android:padding="8dp"> <ImageButton android:id="@+id/btn_play_pause" android:layout_width="48dp" android:layout_height="48dp" android:background="?attr/selectableItemBackgroundBorderless" android:src="@drawable/ic_play" /> <SeekBar android:id="@+id/video_seekbar" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" /> <TextView android:id="@+id/tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="00:00/00:00" android:textColor="@android:color/white" android:textSize="14sp" /> </LinearLayout> </RelativeLayout> </RelativeLayout> ``` ### 修改说明: 1. **移除图片网格容器**: ```diff - <ScrollView>...</ScrollView> + <RelativeLayout>...</RelativeLayout> <!-- 视频播放模块 --> ``` 2. **添加视频播放模块**: ```xml <RelativeLayout android:layout_width="match_parent" android:layout_height="380dp" android:layout_above="@id/footer_buttons" android:layout_below="@id/toolbar" android:layout_marginBottom="298dp"> ``` - 高度设置为380dp - 位置在标题栏下方和底部按钮上方 - 底部边距298dp 3. **添加视频播放器**: ```xml <VideoView android:id="@+id/video_player" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` - 全屏显示视频内容 4. **添加播放控制条**: ```xml <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true"> <!-- 播放/暂停按钮 --> <!-- 进度条 --> <!-- 时间显示 --> </LinearLayout> ``` - 底部定位的播放控制面板 - 半透明背景提升可读性 ### Java代码实现视频播放功能 在Activity中添加视频播放逻辑: ```java public class VideoPlayerActivity extends AppCompatActivity { private VideoView videoPlayer; private ImageButton btnPlayPause; private SeekBar videoSeekbar; private TextView tvTime; private boolean isPlaying = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); // 初始化视图 videoPlayer = findViewById(R.id.video_player); btnPlayPause = findViewById(R.id.btn_play_pause); videoSeekbar = findViewById(R.id.video_seekbar); tvTime = findViewById(R.id.tv_time); // 设置视频源 Uri videoUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.sample_video); videoPlayer.setVideoURI(videoUri); // 播放/暂停按钮点击事件 btnPlayPause.setOnClickListener(v -> togglePlayPause()); // 进度条监听 videoSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { videoPlayer.seekTo(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { videoPlayer.pause(); } @Override public void onStopTrackingTouch(SeekBar seekBar) { videoPlayer.start(); } }); // 视频准备监听 videoPlayer.setOnPreparedListener(mp -> { int duration = mp.getDuration(); videoSeekbar.setMax(duration); updateTimeDisplay(0, duration); }); // 视频播放进度更新 videoPlayer.setOnInfoListener((mp, what, extra) -> { if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { // 缓冲结束处理 } return true; }); } // 播放/暂停切换 private void togglePlayPause() { if (isPlaying) { videoPlayer.pause(); btnPlayPause.setImageResource(R.drawable.ic_play); } else { videoPlayer.start(); btnPlayPause.setImageResource(R.drawable.ic_pause); // 启动进度更新线程 new Thread(this::updateProgress).start(); } isPlaying = !isPlaying; } // 更新播放进度 private void updateProgress() { while (isPlaying) { runOnUiThread(() -> { int currentPosition = videoPlayer.getCurrentPosition(); int duration = videoPlayer.getDuration(); videoSeekbar.setProgress(currentPosition); updateTimeDisplay(currentPosition, duration); }); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } // 更新时间显示 private void updateTimeDisplay(int current, int total) { String currentTime = formatTime(current); String totalTime = formatTime(total); tvTime.setText(currentTime + "/" + totalTime); } // 格式化时间(毫秒 -> mm:ss) private String formatTime(int milliseconds) { int seconds = milliseconds / 1000; int minutes = seconds / 60; seconds = seconds % 60; return String.format("%02d:%02d", minutes, seconds); } } ``` ### 功能说明: 1. **视频播放控制**: - 播放/暂停切换 - 进度条拖动定位 - 实时时间显示 2. **进度更新机制**: - 后台线程每秒更新进度 - 缓冲状态处理 3. **用户体验优化**: - 格式化时间显示 - 缓冲提示 - 响应式控制面板 ### 布局优化建议: 1. **全屏播放支持**: ```java // 在Activity中 getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); ``` 2. **使用ExoPlayer替代VideoView**: ```gradle // build.gradle implementation 'com.google.android.exoplayer:exoplayer:2.18.1' ``` 3. **添加缓冲指示器**: ```xml <ProgressBar android:id="@+id/progress_buffer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="gone" /> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值