我有一个显示卡片视图的带有GridLayoutManager的RecyclerView。我希望卡片根据屏幕大小重新排列(Google Play应用程序使用其应用程序卡片做这种事情)。这里有一个例子:
以下是我的应用程序目前的外观:
如您所见,卡片只是拉伸,不适合由方向更改形成的空白空间。那么我该怎么做呢?
代码:
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>
我试了@Riten回答,效果很好!!但是我对硬编码的“180”不满意,所以我修改为:
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);
}
}
其中“viewId”是在RecyclerView中用作视图的布局,如R.layout.item_for_recycler
虽然不确定View.inflate的影响,因为我只使用它来获得宽度,没有别的。
然后在GridLayoutManager上,我做:
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;
}
}
GridLayoutManager的构造函数有一个参数spanCount
,即
网格中的列数
您可以使用整数资源值初始化管理器,并为不同的屏幕提供不同的值(即< code>values-w600 、< code>values-large 、< code>values-land)。
您可以在给定所需列宽的情况下计算可用列数,并按计算结果加载图像。定义要计算的静态函数为:
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);
问题内容: 我想在Python的xlsxwriter中模拟Excel自动拟合功能。根据此网址,不直接支持它:http : //xlsxwriter.readthedocs.io/worksheet.html 但是,在工作表中的每个单元格之间循环并确定列的最大大小,然后使用worksheet.set_column(row,col,width)设置宽度应该非常简单。 使我无法撰写本文的复杂因素包括:
使用新的GridLayoutManager:https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager.html 它需要一个明确的跨度计数,所以现在的问题是:如何知道每行适合多少“跨度”?毕竟,这是一个网格。根据测量的宽度,RecyclerView应该有尽可能多的跨度。 使用旧的,只需设置“
我寻找过根据textView的大小自动拟合字体的解决方案,找到了很多,但没有一个支持多行并且正确地做到这一点(不截断文本,也尊重重力值)。 有没有其他人进行过这样的解决方案?
如何使用recyclerview实现水平gridlayoutmanager。固定行数。和水平卷轴。这样地。。。 我试着这么做。但这并没有显示项目中的任何内容。
我有4个专栏。第1和第4列的实际含量为150px,第2列为250px,第3列为370px。我想在浏览器宽度改变时换行这些列。当我减小浏览器的宽度时,我希望每列在换行之前收缩到它们的最低宽度。所以我想象第4列在其宽度低于150px之后会以100%的宽度落到下一行。 以下是我认为应该做到的: 有没有一种方法可以做到这一点,而不需要在“max-content”所在的位置传递固定的宽度? 下面是我使用媒体
我读了很多关于如何模拟Spring的bean和它们的自动连线场的文章。但我找不到任何关于豆子的autowired列表。 具体问题 我想测试一下这门课。但我找不到模拟属性的方法。 我所尝试的 由于是单例,我尝试模拟这些bean的几个实例,希望它们能够反映在中,但没有成功。 然后,我尝试创建一个列表,该列表被注释为。通过手动启动,我希望注入创建的列表。这仍然没有成功。 在中抛出了一个NPE,我认为这会