Android GridLayoutManager-如何自动调整列?

Android GridLayoutManager-如何自动调整列?,android,layout,xamarin,android-recyclerview,android-gridlayout,Android,Layout,Xamarin,Android Recyclerview,Android Gridlayout,我有一个带有GridLayoutManager的RecyclerView,可以显示卡片视图。我想根据屏幕大小重新排列卡片(谷歌Play应用程序使用其应用程序卡片进行此类操作)。以下是一个例子: 以下是我的应用程序目前的外观: 正如你所看到的,这些卡片只是拉伸,不适合因方向改变而产生的空白。那我该怎么做呢 代码: 使用系统; 使用System.Collections.Generic; 使用System.Linq; 使用系统文本; 使用System.Json; 使用系统线程; 使用Syste

我有一个带有GridLayoutManager的RecyclerView,可以显示卡片视图。我想根据屏幕大小重新排列卡片(谷歌Play应用程序使用其应用程序卡片进行此类操作)。以下是一个例子:

以下是我的应用程序目前的外观:

正如你所看到的,这些卡片只是拉伸,不适合因方向改变而产生的空白。那我该怎么做呢

代码:

使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Json;
使用系统线程;
使用System.Threading.Tasks;
使用Android.Media;
使用Android.App;
使用Android.Support.V4.App;
使用Android.Support.V4.Content.Res;
使用Android.Support.V4.Widget;
使用Android.Support.V7.Widget;
使用Android.Content;
使用Android.OS;
使用Android.Runtime;
使用Android.Util;
使用Android.Views;
使用Android.Widget;
使用Android.Net;
使用Android.Views.Animations;
使用Android.Graphics;
使用Android.Graphics.Drawables;
使用Newtonsoft.Json;
使用******适配器;
使用******模型;
命名空间******.Fragments{
公共类仪表板:GridLayoutBase{
私人iSharedReferences优先;
私人会话管理器会话;
私有字符串cookie;
私有设备模型设备模型;
私人回收站;
专用循环视图适配器;
//private RecyclerView.LayoutManager LayoutManager;
专用GridLayoutManager GridLayoutManager;
私有列表项数据;
私有位图;
私人可拉丝、可拉丝;
私人静态活动;
私有进程对话;
私有文本视图节点数据;
私有常量字符串URL_仪表板=”http://192.168.1.101/appapi/getdashboard";
私有常量字符串URL_数据=”http://192.168.1.101/appapi/getdata";
创建时公共覆盖无效(捆绑包){
base.OnCreate(bundle);
活动=活动;
会话=新建会话管理器();
pref=activity.getSharedReferences(“UserSession”,FileCreationMode.Private);
cookie=pref.GetString(“PHPSESSID”,string.Empty);
}
公共异步重写void OnStart(){
base.OnStart();
progressDialog=progressDialog.Show(activity,String.Empty,GetString(Resource.String.loading_text));
progressDialog.Window.ClearFlags(WindowManagerFlags.DimBehind);
等待GetDeviceInfo();
如果(deviceModel.Error==“true”&&deviceModel.ErrorType==“noSensors”){
recyclerView.Visibility=ViewStates.Gone;
noData.Visibility=ViewStates.Visible;
progressDialog.Hide();
返回;
}否则{
recyclerView.Visibility=ViewStates.Visible;
noData.Visibility=ViewStates.Gone;
等待大众意识状态();
}
//DisplayLastPhoto();
适配器=新的ViewAdapter(itemData);
新System.Threading.Thread(新System.Threading.ThreadStart(()=>{
activity.RunOnUiThread(()=>{
recyclerView.SetAdapter(适配器);
});
})).Start();
progressDialog.Hide();
}
公共异步任务getDeviceInfo(){
var jsonFetcher=new jsonFetcher();
JsonValue jsonDashboard=等待jsonFetcher.FetchDataWithCookieAAsync(URL\u仪表板,cookie);
deviceModel=新的deviceModel();
deviceModel=JsonConvert.DeserializeObject(jsonDashboard);
}
//显示传感器状态
公共异步任务PopulateSensorStates(){
itemData=新列表();
string lastValue=string.Empty;
foreach(此设备模型传感器中的var传感器){
var sensorImage=ResourcesCompat.GetDrawable(Resources,Resource.Drawable.smoke\u red,null);
开关(传感器类型){
案例“2”:
var jsonFetcher=new jsonFetcher();
JsonValue jsonData=await jsonFetcher.FetchSensorDataAsync(URL_数据,sensor.Id,“DESC”,“1”,cookie);
var deviceModel=new deviceModel();
deviceModel=JsonConvert.DeserializeObject(jsonData);
lastValue=deviceModel.SensorData.Last().Value;
打破
案例“4”:
等待RenderLastCameraPhoto();
sensorImage=新的位图绘制(参考资料,lastPhotoBitmap);
打破
}
添加(新的itemData(){
id=传感器id,
value=lastValue,
类型=传感器。类型,
图像=传感器图像,
title=sensor.Name.First().ToString().ToUpper()+sensor.Name.Substring(1.ToLower(),
});
}
}
//显示最后一张相机照片
公共异步任务RenderLastCameraPhoto(){
如果(deviceModel.Error==“true”&&deviceModel.ErrorType==“noPhoto”){
//TODO:显示“无照片”图片
}否则{
字符串url=deviceModel.LastPhotoLink;
lastPhotoBitmap=Wait new ImageDownloader().GetImageBitmapFromUrlAsync(url,activity,300300);
}
}
公共异步void UpdateData(bool-isSwipeRefresh){
等待GetDeviceInfo();
如果(deviceModel.Error==“true”&&deviceModel.ErrorType==“noSensors”){
recyclerView.Visibility=ViewStates.Gone;
noData.Visibility=ViewStates.Visible;
返回;
}否则{
recyclerView.Visibility=ViewStates.Visible;
noData.可视性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Json;
using System.Threading;
using System.Threading.Tasks;
using Android.Media;
using Android.App;
using Android.Support.V4.App;
using Android.Support.V4.Content.Res;
using Android.Support.V4.Widget;
using Android.Support.V7.Widget;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Android.Net;
using Android.Views.Animations;
using Android.Graphics;
using Android.Graphics.Drawables;
using Newtonsoft.Json;
using *******.Adapters;
using *******.Models;

namespace *******.Fragments {
    public class Dashboard : GridLayoutBase {
        private ISharedPreferences pref;
        private SessionManager session;
        private string cookie;
        private DeviceModel deviceModel;
        private RecyclerView recyclerView;
        private RecyclerView.Adapter adapter;
//      private RecyclerView.LayoutManager layoutManager;
        private GridLayoutManager gridLayoutManager;
        private List<ItemData> itemData;
        private Bitmap lastPhotoBitmap;
        private Drawable lastPhotoDrawable;
        private static Activity activity;
        private ProgressDialog progressDialog;
        private TextView noData;
        private const string URL_DASHBOARD = "http://192.168.1.101/appapi/getdashboard";
        private const string URL_DATA = "http://192.168.1.101/appapi/getdata";

        public override void OnCreate(Bundle bundle) {
            base.OnCreate(bundle);

            activity = Activity;
            session = new SessionManager();
            pref = activity.GetSharedPreferences("UserSession", FileCreationMode.Private);
            cookie = pref.GetString("PHPSESSID", string.Empty);
        }

        public async override void OnStart() {
            base.OnStart();

            progressDialog = ProgressDialog.Show(activity, String.Empty, GetString(Resource.String.loading_text));
            progressDialog.Window.ClearFlags(WindowManagerFlags.DimBehind);

            await GetDevicesInfo();

            if (deviceModel.Error == "true" && deviceModel.ErrorType == "noSensors") {
                recyclerView.Visibility = ViewStates.Gone;
                noData.Visibility = ViewStates.Visible;

                progressDialog.Hide();

                return;
            } else {
                recyclerView.Visibility = ViewStates.Visible;
                noData.Visibility = ViewStates.Gone;

                await PopulateSensorStates();
            }

//          DisplayLastPhoto();

            adapter = new ViewAdapter(itemData);

            new System.Threading.Thread(new System.Threading.ThreadStart(() => {
                activity.RunOnUiThread(() => {
                    recyclerView.SetAdapter(adapter);
                });
            })).Start();

            progressDialog.Hide();
        }

        public async Task GetDevicesInfo() {
            var jsonFetcher = new JsonFetcher();
            JsonValue jsonDashboard = await jsonFetcher.FetchDataWithCookieAsync(URL_DASHBOARD, cookie);
            deviceModel = new DeviceModel();
            deviceModel = JsonConvert.DeserializeObject<DeviceModel>(jsonDashboard);
        }

        // Shows sensor states
        public async Task PopulateSensorStates() {
            itemData = new List<ItemData>();
            string lastValue = String.Empty;

            foreach (var sensor in this.deviceModel.Sensors) {
                var sensorImage = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.smoke_red, null);

                switch (sensor.Type) {
                case "2":
                    var jsonFetcher = new JsonFetcher();
                    JsonValue jsonData = await jsonFetcher.FetchSensorDataAsync(URL_DATA, sensor.Id, "DESC", "1", cookie);
                    var deviceModel = new DeviceModel();
                    deviceModel = JsonConvert.DeserializeObject<DeviceModel>(jsonData);
                    lastValue = deviceModel.SensorData.Last().Value;
                    break;
                case "4":
                    await RenderLastCameraPhoto();
                    sensorImage = new BitmapDrawable(Resources, lastPhotoBitmap);
                    break;
                }

                itemData.Add(new ItemData() {
                    id = sensor.Id,
                    value = lastValue,
                    type = sensor.Type,
                    image = sensorImage,
                    title = sensor.Name.First().ToString().ToUpper() + sensor.Name.Substring(1).ToLower(),
                });
            }
        }

        // Shows the last camera photo
        public async Task RenderLastCameraPhoto() {
            if (deviceModel.Error == "true" && deviceModel.ErrorType == "noPhoto") {
                //TODO: Show a "No photo" picture
            } else {
                string url = deviceModel.LastPhotoLink;
                lastPhotoBitmap = await new ImageDownloader().GetImageBitmapFromUrlAsync(url, activity, 300, 300);
            }
        }

        public async void UpdateData(bool isSwipeRefresh) {
            await GetDevicesInfo();

            if (deviceModel.Error == "true" && deviceModel.ErrorType == "noSensors") {
                recyclerView.Visibility = ViewStates.Gone;
                noData.Visibility = ViewStates.Visible;

                return;
                } else {
                recyclerView.Visibility = ViewStates.Visible;
                noData.Visibility = ViewStates.Gone;

                await PopulateSensorStates();
            }

            adapter = new ViewAdapter(itemData);

            new System.Threading.Thread(new System.Threading.ThreadStart(() => {
                activity.RunOnUiThread(() => {
                    recyclerView.SetAdapter(adapter);
                });
            })).Start();

            adapter.NotifyDataSetChanged();
        }

        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.Inflate(Resource.Layout.Dashboard, container, false);
            noData = view.FindViewById<TextView>(Resource.Id.no_data_title);

            SwipeRefreshLayout swipeRefreshLayout = view.FindViewById<SwipeRefreshLayout>(Resource.Id.swipe_container);
            //          swipeRefreshLayout.SetColorSchemeResources(Color.LightBlue, Color.LightGreen, Color.Orange, Color.Red);

            // On refresh button press/swipe, updates the recycler view with new data
            swipeRefreshLayout.Refresh += (sender, e) => {
                UpdateData(true);

                swipeRefreshLayout.Refreshing = false;
            };

            var gridLayoutManager = new GridLayoutManager(activity, 2);

            recyclerView = view.FindViewById<RecyclerView>(Resource.Id.dashboard_recycler_view);
            recyclerView.HasFixedSize = true;
            recyclerView.SetLayoutManager(gridLayoutManager);
            recyclerView.SetItemAnimator(new DefaultItemAnimator());
            recyclerView.AddItemDecoration(new SpaceItemDecoration(15));

            return view;
        }

        public class ViewAdapter : RecyclerView.Adapter {
            private List<ItemData> itemData;
            public string sensorId;
            public string sensorType;
            private ImageView imageId;
            private TextView sensorValue;
            private TextView sensorTitle;

            public ViewAdapter(List<ItemData> itemData) {
                this.itemData = itemData;
            }

            public class ItemView : RecyclerView.ViewHolder {
                public View mainView { get; set; }

                public string id { get; set; }

                public string type { get; set; }

                public ImageView image { get; set; }

                //              public TextView value { get; set; }

                public TextView title { get; set; }

                public ItemView(View view) : base(view) {
                    mainView = view;
                }
            }

            public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) {
                View itemLayoutView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.DashboardItems, null);
                CardView cardView = itemLayoutView.FindViewById<CardView>(Resource.Id.dashboard_card_view);
                imageId = itemLayoutView.FindViewById<ImageView>(Resource.Id.sensor_image);
//              sensorValue = itemLayoutView.FindViewById<TextView>(Resource.Id.sensor_value);
                sensorTitle = itemLayoutView.FindViewById<TextView>(Resource.Id.sensor_title);

                var viewHolder = new ItemView(itemLayoutView) {
                    id = sensorId,
                    type = sensorType,
                    image = imageId,
//                  value = sensorValue,
                    title = sensorTitle
                };

                return viewHolder;
            }

            public override void OnBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
                ItemView itemHolder = viewHolder as ItemView;

                itemHolder.image.SetImageDrawable(itemData[position].image);

                if (itemData[position].type == "2") { // Temperature
                    itemHolder.title.Text = itemData[position].title + ": " + itemData[position].value;
                } else {
                    itemHolder.title.Text = itemData[position].title;
                }

                var bundle = new Bundle();
                var dualColumnList = new DualColumnList();
                var gallery = new Gallery();

                EventHandler clickUpdateViewEvent = ((sender, e) => {
                    bundle.PutString("id", itemData[position].id);
                    gallery.Arguments = bundle;
                    dualColumnList.Arguments = bundle;

                    if (itemData[position].type == "4") { // Camera
                        ((FragmentActivity)activity).ShowFragment(gallery, itemData[position].title, itemData[position].type, true);
                    } else {
                        ((FragmentActivity)activity).ShowFragment(dualColumnList, itemData[position].title, itemData[position].type, true);
                    }
                });

                itemHolder.image.Click += clickUpdateViewEvent;
//              itemHolder.value.Click += clickUpdateViewEvent;
                itemHolder.title.Click += clickUpdateViewEvent;
            }

            public override int ItemCount {
                get { return itemData.Count; }
            }
        }

        public class ItemData {
            public string id { get; set; }

            public string type { get; set; }

            public Drawable image { get; set; }

            public string value { get; set; }

            public string title { get; set; }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:weightSum="1">
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="0.9"
            android:scrollbars="vertical">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/dashboard_recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
            <TextView
                android:text="@string/no_data_text"
                android:id="@+id/no_data_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="30sp"
                android:gravity="center"
                android:layout_centerInParent="true" />
        </RelativeLayout>
    </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dashboard_card_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:foreground="?android:attr/selectableItemBackground">
        <ImageView
            android:id="@+id/sensor_image"
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:paddingTop="5dp"
            android:layout_alignParentTop="true" />
    <!--        <TextView
            android:id="@+id/sensor_value"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="30sp"
            android:layout_below="@id/sensor_image"
            android:gravity="center" />-->
        <TextView
            android:id="@+id/sensor_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="23sp"
            android:layout_below="@id/sensor_image"
            android:gravity="center"
            android:layout_alignParentBottom="true" />
    </LinearLayout>
</android.support.v7.widget.CardView>
public class Utility {
    public static int calculateNoOfColumns(Context context, float columnWidthDp) { // For example columnWidthdp=180
        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        float screenWidthDp = displayMetrics.widthPixels / displayMetrics.density;
        int noOfColumns = (int) (screenWidthDp / columnWidthDp + 0.5); // +0.5 for correct rounding to int.
        return noOfColumns;
    }
}
int mNoOfColumns = Utility.calculateNoOfColumns(getApplicationContext());

............
mGridLayoutManager = new GridLayoutManager(this, mNoOfColumns);
    public class ColumnQty {
    private int width, height, remaining;
    private DisplayMetrics displayMetrics;

    public ColumnQty(Context context, int viewId) {

        View view = View.inflate(context, viewId, null);
        view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        width = view.getMeasuredWidth();
        height = view.getMeasuredHeight();
        displayMetrics = context.getResources().getDisplayMetrics();
    }
    public int calculateNoOfColumns() {

        int numberOfColumns = displayMetrics.widthPixels / width;
        remaining = displayMetrics.widthPixels - (numberOfColumns * width);
//        System.out.println("\nRemaining\t" + remaining + "\nNumber Of Columns\t" + numberOfColumns);
        if (remaining / (2 * numberOfColumns) < 15) {
            numberOfColumns--;
            remaining = displayMetrics.widthPixels - (numberOfColumns * width);
        }
        return numberOfColumns;
    }
    public int calculateSpacing() {

        int numberOfColumns = calculateNoOfColumns();
//        System.out.println("\nNumber Of Columns\t"+ numberOfColumns+"\nRemaining Space\t"+remaining+"\nSpacing\t"+remaining/(2*numberOfColumns)+"\nWidth\t"+width+"\nHeight\t"+height+"\nDisplay DPI\t"+displayMetrics.densityDpi+"\nDisplay Metrics Width\t"+displayMetrics.widthPixels);
        return remaining / (2 * numberOfColumns);
    }
}
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, Utility.columnQty(this, R.layout.item_for_recycler));
recyclerPatternsView.addItemDecoration(new GridSpacing(columnQty.calculateSpacing()));
public class GridSpacing extends RecyclerView.ItemDecoration {
    private final int spacing;

    public GridSpacing(int spacing) {
        this.spacing = spacing;
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        outRect.left = spacing;
        outRect.right = spacing;
        outRect.bottom = spacing;
        outRect.top = spacing;
    }
}
public int getNumberOfColumns() {
        View view = View.inflate(this, R.layout.row_layout, null);
        view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        int width = view.getMeasuredWidth();
        int count = getResources().getDisplayMetrics().widthPixels / width;
        int remaining = getResources().getDisplayMetrics().widthPixels - width * count;
        if (remaining > width - 15)
            count++;
        return count;
    }
public class AutoFitGridLayoutManager extends GridLayoutManager {

private int columnWidth;
private boolean columnWidthChanged = true;

public AutoFitGridLayoutManager(Context context, int columnWidth) {
    super(context, 1);

    setColumnWidth(columnWidth);
}


public void setColumnWidth(int newColumnWidth) {
    if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
        columnWidth = newColumnWidth;
        columnWidthChanged = true;
    }
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    if (columnWidthChanged && columnWidth > 0) {
        int totalSpace;
        if (getOrientation() == VERTICAL) {
            totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
        } else {
            totalSpace = getHeight() - getPaddingTop() - getPaddingBottom();
        }
        int spanCount = Math.max(1, totalSpace / columnWidth);
        setSpanCount(spanCount);
        columnWidthChanged = false;
    }
    super.onLayoutChildren(recycler, state);
}
 AutoFitGridLayoutManager layoutManager = new AutoFitGridLayoutManager(this, 250);
 recycleView.setLayoutManager(layoutManager)
recyclerView.setLayoutManager(new GridLayoutManager(this, 4));
/**
 * @param columnWidth - in dp
 */
fun RecyclerView.autoFitColumns(columnWidth: Int) {
    val displayMetrics = this.context.resources.displayMetrics
    val noOfColumns = ((displayMetrics.widthPixels / displayMetrics.density) / columnWidth).toInt()
    this.layoutManager = GridLayoutManager(this.context, noOfColumns)
}
myRecyclerView.autoFitColumns(200)