说明:这个简单的基于RecyclerView的框架作用在于自己可以将平时积累的一些有效demo整合起来(比如音视频编解码的、opengles的以及其他也去方向的、随着项目增多,工程量的增加,后期想高效的分析和查找并不容易),不用搞太多的工程,不像多个工程过于分散也占空间。
1 基于RecyclerView框架工程实现原理说明
该工程通过config.xml配置文件获取每一个网格中的基本信息,并将其存储到itemlist中。应用启动后,点击网格中的按键,每个按键可以按需启动一个应用。适用于长期积累自己的知识体系。
接下来直接上干货,粘过去可以直接用的那种~。平台是基于Android12的。
2 框架工程代码完整解读(android Q)
2.1 layout布局文件解读
res/layout 主界面 activity_main.xml内容如下:
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:ignore="ExtraText">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="500dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textviewMainMenu"
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="@string/title"
android:background="@color/white"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/recyclerview" />
</androidx.constraintlayout.widget.ConstraintLayout>
RecycleView中需要使用的配置文件item参考实现如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="18dp"
android:text="@string/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/h264_decode_demo"
app:layout_constraintStart_toStartOf="@+id/textView"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.2 配置文件体系构建
使用res/xml 配置文件 config.xml内容如下:
<?xml version="1.0" encoding="utf-8"?>
<items>
<item>
<description>H264解码一个download目录下的out.h264码流</description>
<buttonName>H264解码</buttonName>
<activityName>com.wds.videoexample.Activity1</activityName>
</item>
<item>
<description>通过mediaprojection获取投屏数据,使用H264编码一个sdcard/路径下的codec.h264码流</description>
<buttonName>H264编码</buttonName>
<activityName>com.example.app.Activity2</activityName>
</item>
<item>
<description>通过camerax获取投屏数据,使用H264编码一个sdcard/路径下的codec.h264码流</description>
<buttonName>Button03</buttonName>
<activityName>com.example.app.Activity3</activityName>
</item>
。。。
<items>
关于元素的个数和tag,大家可以自己按照自己的需求自定义。接下来有了配置文件,还要有配套的解析器,这里命名为ParseConfig,代码实现如下:
public class ParserConfig {
private final static String TAG = "ParserConfig";
private static List<Item> itemList;
public static List<Item> getItemList() {
return itemList;
}
private static String fullDescription = "";
@SuppressLint("DefaultLocale")
public static void initItemList(Context context) {
itemList = new ArrayList<>();
XmlResourceParser parser = context.getResources().getXml(R.xml.config);
Log.d(TAG,"initItemLis");
String description = null;
String buttonName = null;
String activityName = null;
String tagName = null;
//String fullDescription = "";
int index= 0;
try {
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
tagName = parser.getName();
if ("item".equals(tagName)) {
index++;
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
description = parser.getText();
fullDescription += "\ndemo"+String.format("%03d",index)+":"+description+"\n";
}
}
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
buttonName = parser.getText();
}
}
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
activityName = parser.getText();
}
}
if (buttonName != null && activityName != null) {
itemList.add(new Item(description,buttonName, activityName));
}
}
}
eventType = parser.next();
}
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
}
public static String getFullDescription(){
return fullDescription;
}
}
这里涉及的itemlist中的元素item定义如下:
public class Item {
public String buttonName; //点击按键内容
private String activityName;
private String description;
public Item(String description, String buttonName, String activityName) {
this.buttonName = buttonName;
this.activityName = activityName;
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getButtonName() {
return buttonName;
}
public void setButtonName(String buttonName) {
this.buttonName = buttonName;
}
public String getActivityName() {
return activityName;
}
public void setActivityName(String activityName) {
this.activityName = activityName;
}
}
2.3 权限的处理
关于权限,使用了一个Permission 专门的类来做运行时权限的处理,代码实现如下:
public class Permission {
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 1;
//需要申请权限的数组
private static final String[] permissions = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
};
//保存真正需要去申请的权限
private static final List<String> permissionList = new ArrayList<>();
public static int RequestCode = 100;
public static void requestManageExternalStoragePermission(Context context, Activity activity) {
if (!Environment.isExternalStorageManager()) {
showManageExternalStorageDialog(activity);
}
}
private static void showManageExternalStorageDialog(Activity activity) {
AlertDialog dialog = new AlertDialog.Builder(activity)
.setTitle("权限请求")
.setMessage("请开启文件访问权限,否则应用将无法正常使用。")
.setNegativeButton("取消", null)
.setPositiveButton("确定", (dialogInterface, i) -> {
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
activity.startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE);
})
.create();
dialog.show();
}
public static void checkPermissions(Activity activity) {
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(permission);
}
}
if (!permissionList.isEmpty()) {
requestPermission(activity);
}
}
public static void requestPermission(Activity activity) {
ActivityCompat.requestPermissions(activity,permissionList.toArray(new String[0]),RequestCode);
}
}
这样,如果后面又更多的权限,都可以使用该方法来处理,处理方式为:
Permission.checkPermissions(this);
Permission.requestManageExternalStoragePermission(getApplicationContext(), this);
2.4 基于RecyclerView的框架工程 | 主流程代码参考实现
这里给出框架工程的代码的实现。具体如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private RecyclerView mRecyclerView;
MyAdapter mMyAdapter ;
private List<Item> itemList;
private TextView mTextViewMainmenu;
@SuppressLint("DefaultLocale")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
mTextViewMainmenu = findViewById(R.id.textviewMainMenu);
mTextViewMainmenu.setMovementMethod(ScrollingMovementMethod.getInstance());
mRecyclerView = findViewById(R.id.recyclerview);
ParserConfig.initItemList(this);
mTextViewMainmenu.setText(ParserConfig.getFullDescription());
itemList = ParserConfig.getItemList();
mMyAdapter = new MyAdapter();
mRecyclerView.setAdapter(mMyAdapter);
GridLayoutManager layoutManager = new GridLayoutManager(MainActivity.this,3);
mRecyclerView.setLayoutManager(layoutManager);
DividerItemDecoration mDivider = new
DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
mRecyclerView.addItemDecoration(mDivider);
DividerItemDecoration mDivider2 = new
DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL);
mRecyclerView.addItemDecoration(mDivider2);
}
class MyAdapter extends RecyclerView.Adapter<MyViewHoder> {
@NonNull
@Override
public MyViewHoder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = View.inflate(MainActivity.this, R.layout.item_list, null);
return new MyViewHoder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHoder holder, int position) {
Item item = itemList.get(position);
@SuppressLint("DefaultLocale") String title_text = getString(R.string.wbs_demo) + String.format("%03d", position+1);
holder.mTitleTv.setText(title_text);
holder.mButton.setText(item.buttonName);
holder.mButton.setTag(position);
holder.mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = (int)view.getTag();
mTextViewMainmenu.setText(itemList.get(position).getDescription());
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (position == 0) {
Intent intent = new Intent(MainActivity.this, H264decoderActivity.class);
startActivity(intent);
} else if (position == 1) {
Intent intent = new Intent(MainActivity.this, H264encoderMediaProjActivity.class);
startActivity(intent);
} else if (position == 2) {
Intent intent = new Intent(MainActivity.this, H264encoderCameraXActivity.class);
startActivity(intent);
}
}
}, 3000); //
Log.d(TAG,"onclick"+view.getTag());
}
});
}
@Override
public int getItemCount() {
return itemList.size();
}
}
static class MyViewHoder extends RecyclerView.ViewHolder {
TextView mTitleTv;
Button mButton;
public MyViewHoder(@NonNull View itemView) {
super(itemView);
mTitleTv = itemView.findViewById(R.id.textView);
mButton = itemView.findViewById(R.id.button);
}
}
}
2.5 主框架 demo实现效果
实际运行效果展示如下: