RecyclerView 万能适配在文章末尾
同一个app在平板与手机上的显示,因为屏幕大小的区别,可能需要显示为不同的样式。如下图所示
平板样式
手机样式
在平板状态下,于同一个页面显示两个部分,在手机中则显示在不同页面。
此处采用fragment 来实现。分为几个步骤。
1、首先判断设备是手机还是平板,此处使用官方提供的方法
/**
* 返回 平板 true
* 手机 false
*/
private fun isIPad():Boolean{
return resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK >= Configuration.SCREENLAYOUT_SIZE_LARGE
}
返回值为true时表示当前设备时平板,返回false时表明当前设备时手机。
2、新建资源文件夹 layout-large
系统会自动判断当前的设备情况来加载layout或者layout-large中的资源布局文件,设备是手机时加载的是 layout文件夹下的布局文件,平板时则加载 layout-large 文件夹下的资源文件。
2.1 首先我们新建 两个fragment,用于布局需要
fragment_list 用于列表项加载:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rlv_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
fragment_detail 用于详情加载
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_detail_title"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="555"/>
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:src="@drawable/test_01"/>
</LinearLayout>
此处详情页面简化用于实现效果即可
创建 ListFragment 以及 DetailFragment 备用,其中 ListFragment 对应布局 fragment_list ,DetailFragment 对应布局 fragment_detail 。
由于 ListFragment 的代码相对复杂,在后面给出,此处先给出详情页 DetailFragment 代码
class DetailFragment :Fragment(){
private var fview:View?=null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if(fview==null){
fview= inflater.inflate(R.layout.fragment_detail,null)
}
val parent=fview?.parent
if(parent!=null){
(parent as ViewGroup).removeView(fview)
}
return fview
}
/**
* 开放数据更新接口
*/
fun setValueData(bean:Testbean){
fview?.tv_detail_title!!.text=bean.title
fview?.iv_image!!.setImageResource(bean.image)
}
}
2.2 创建activity的布局文件
在 layout 下新建 布局文件 activity_test.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment_list"
android:name="com.admin.testproject.fragment.ListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
以及activity_detail
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frame_content"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
然后再 在 layout-large 文件夹下创建 activity_test.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/fragment_list"
android:name="com.admin.testproject.fragment.ListFragment"
android:layout_width="0dp"
android:layout_weight="1.0"
android:layout_height="match_parent"/>
<!-- 两个fragment 之间加一条分割线 -->
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#ff0000"/>
<FrameLayout
android:id="@+id/fragment_content"
android:layout_width="0dp"
android:layout_weight="2.0"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
资源文件夹如下图
2.3 处理相关的设置
首先创建 BaseActivity 并设置公共方法,用于判断设备情况以及根据情况设置 设备的横竖屏
abstract class BaseActivity :AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestedOrientation = if(isIPad()){
Log.e("***","isPad")
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
}else{
Log.e("***","isPhone")
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
}
setContentView(bindLayout())
bindData()
bindEvent()
}
abstract fun bindLayout():Int
abstract fun bindData()
abstract fun bindEvent()
/**
* 返回 平板 true
* 手机 false
*/
private fun isIPad():Boolean{
return resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK >= Configuration.SCREENLAYOUT_SIZE_LARGE
}
}
然后所有的 activity都继承 BaseActivity 。
新建 TestActivity 加载布局文件 activity_test
class TestActivity:BaseActivity(){
override fun bindLayout(): Int {
return R.layout.activity_test
}
override fun bindData() {
}
override fun bindEvent() {
}
}
我们在 ListFragment 处理 单双屏的判断问题。
在 两个 activity_test.xml 文件中,我们的布局文件是有差异的,平板加载的布局文件比手机加载的布局文件多了一个FragmeLayout,我们通过判断这个 FragmeLayout 是否存在来判断是否为双屏
判断的方法为
isTwopage = activity!!.findViewById<FrameLayout>(R.id.fragment_content)!=null
isTwopage 为true时表示双屏(平板),false时为单屏(手机)
然后我们初始化ListFragment 的测试数据数据,ListFragment 的代码为
class ListFragment :Fragment(){
private var fview:View?=null
private var isTwopage=false
private val titles= arrayOf("title 001","title 002","title 003","title 004","title 005")
private val images= arrayOf(R.drawable.test_01,R.drawable.test_02,R.drawable.test_03,R.drawable.test_04,R.drawable.test_05)
private val data=ArrayList<Testbean>()
private var adapter:TestAdapter?=null
private var detailFragment:DetailFragment?=null
@SuppressLint("InflateParams")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if(fview==null){
fview= inflater.inflate(R.layout.fragment_list,null)
initData()
}
val parent=fview?.parent
if(parent!=null){
(parent as ViewGroup).removeView(fview)
}
return fview
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
isTwopage = activity!!.findViewById<FrameLayout>(R.id.fragment_content)!=null
if(isTwopage){
//如果是双屏,加载 DetailFragment
detailFragment= DetailFragment()
activity!!.supportFragmentManager.beginTransaction().replace(R.id.fragment_content,detailFragment!!).commit()
}
Log.e("****","$isTwopage")
}
private fun initData(){
for(i in 0 until titles.size){
data.add(Testbean(titles[i],images[i]))
}
adapter= TestAdapter(activity!!)
fview!!.rlv_list.layoutManager=LinearLayoutManager(activity!!)
fview!!.rlv_list.adapter=adapter
adapter?.upData(data)
adapter?.onItemClick= object : OnItemClickListener<Testbean> {
override fun onItemClick(item: Testbean) {
if(isTwopage){
//双屏直接更新数据
click(item)
}else{
//单屏直接跳转到详情activity
val intent=Intent(activity!!,NewDetailActivity::class.java)
val bundle=Bundle()
bundle.putSerializable("bean",item)
intent.putExtras(bundle)
startActivity(intent)
}
}
}
}
override fun onStart() {
super.onStart()
if(isTwopage){
//双屏初始化详情页数据
click(data[0])
}
}
fun click(item: Testbean){
detailFragment?.setValueData(item)
}
}
Testbean 的代码
class Testbean ( var title:String,var image:Int):Serializable{
}
DetailActivity的代码
class DetailActivity :BaseActivity(){
val detailFragmrnt=DetailFragment()
val handler=Handler()
override fun bindLayout(): Int {
return R.layout.activity_new_detail
}
override fun bindData() {
supportFragmentManager.beginTransaction().replace(R.id.frame_content,detailFragmrnt).commit()
}
override fun bindEvent() {
val title=intent.getSerializableExtra("bean") as Testbean
handler.postDelayed({
detailFragmrnt.setValueData(title)
},10)
}
}
附上代码运行的效果,比开头的详细一点:
手机效果
平板效果与开头的一致,不过还是放一下
测试的图片就不放了。下面给出的代码是 RecyclerView 的万能适配 器
RecycleBaseAdapter
public abstract class RecycleBaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
private List<T> data;
public Context context;
public OnItemClickListener onItemClick;
public abstract int getItemLayout(int position);
public void upData(List<T> data) {
this.data = data;
notifyDataSetChanged();
}
public RecycleBaseAdapter(Context context){
this.context=context;
}
@Override
public int getItemViewType(int position) {
//可以通过修改此处的返回值,来改变 需要加载的 页面类型,
// 然后在getItemLayout中判断相同的值的时候返回所需的 布局ID
return position;
}
@NonNull
@Override
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return BaseViewHolder.getHolder(viewGroup,getItemLayout(i));
}
@Override
public void onBindViewHolder(@NonNull BaseViewHolder viewHolder, int i) {
if(viewHolder!=null && data.get(i)!=null) {
initData(viewHolder, data.get(i), i);
}
}
public abstract void initData(BaseViewHolder viewHolder, T item, int position);
@Override
public int getItemCount() {
return data==null?0:data.size();
}
}
BaseViewHolder
public class BaseViewHolder extends RecyclerView.ViewHolder{
private SparseArray<View> mviews;
private View mContentView;
private BaseViewHolder(@NonNull View itemView) {
super(itemView);
mContentView=itemView;
mviews=new SparseArray<View>();
}
static BaseViewHolder getHolder(ViewGroup parent, int layoutId){
View view= LayoutInflater.from(parent.getContext()).inflate(layoutId,parent,false);
return new BaseViewHolder(view);
}
public <T extends View> T getView(int viewId){
View view=mviews.get(viewId);
if(view==null){
view=mContentView.findViewById(viewId);
mviews.put(viewId,view);
}
return (T)view;
}
}
以上是 RecyclerView 的通用适配代码,下面是本文中的TestAdapter代码
class TestAdapter (context: Context):RecycleBaseAdapter<Testbean>(context){
override fun getItemLayout(position: Int): Int {
return R.layout.layout_recy_item
}
override fun initData(viewHolder: BaseViewHolder?, item: Testbean?, position: Int) {
viewHolder?.getView<TextView>(R.id.tv_item)?.text=item?.title
viewHolder?.getView<ImageView>(R.id.iv_title)?.setImageResource(item?.image!!)
viewHolder?.getView<LinearLayout>(R.id.linear_item)?.setOnClickListener {
onItemClick?.onItemClick(item)
}
}
}
使用起来就是如此简单。
注:中代码混合了 java 与 kotlin