2015-11-06 1231 views
24

我有一个显示卡片视图的GridLayoutManager的RecyclerView。我希望卡片根据屏幕大小重新排列(Google Play应用使用它的应用卡做这种事情)。这里有一个例子:GridLayoutManager - 如何自动适应列?

enter image description here

enter image description here

这里是我的应用程序是怎样看待的时刻:

enter image description here

enter image description here

正如你可以看到卡伸展并且不适合从东方制成的空白空间改变。那我该怎么做?

代码:

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> 
+0

嘿,你有没有得到解决方案?我也卡住 –

+0

对我来说,当我给实际宽度recyclerView,它的项目autofit。 –

回答

49

您可以计算列数并加载计算得出的图像。定义一个静态funtion来计算:

public class Utility { 
    public static int calculateNoOfColumns(Context context) { 
     DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 
     float dpWidth = displayMetrics.widthPixels/displayMetrics.density; 
     int noOfColumns = (int) (dpWidth/180); 
     return noOfColumns; 
    } 
} 

然后在活动或片段使用它的时候,你可以这样做:

int mNoOfColumns = Utility.calculateNoOfColumns(getApplicationContext()); 

............ 
mGridLayoutManager = new GridLayoutManager(this, mNoOfColumns); 
+0

真棒代码。适合我的作品。当我用140代替180. – Abhishek

+0

答案中的'180'是什么? – khateeb

+2

180是您的网格项目的宽度。您可以按照您的惯例更改它。 – Ariq

1

构造new GridLayoutManager(activity, 2)约为GridLayoutManager(Context context, int spanCount)其中spanCount是在网格中的列数。

最好的方法是检查窗口/视图宽度,并根据此宽度计算您要显示的跨度数。

3

我试图@Riten答案和工作FUNTASTIC!但我却高兴不起来与硬编码“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)); 

UPDATE: 我添加更多的行中的代码,我用它来获得在网格的最小宽度的间距。 计算间距:

recyclerPatternsView.addItemDecoration(new GridSpacing(columnQty.calculateSpacing())); 
+0

嗨RACU,我想你的方法,但'view.getMeasuredWidth()'总是返回0。任何想法? – Sam

+0

不知道,我只能认为可能是xml,你可能有“0dp”。 我会更新整个班级,因为我做了一些更改。 – Racu

+0

@Sam我更新到我的最新版本,请注意,我也使用它来设置项目之间的距离。那里的“15”确保了项目之间的空间至少为15px,如果它较少,该方法将删除一列并重新计算间距。 希望我可以制作“15px”“15dp”或更少的硬编码。 GL – Racu