24

Tôi có RecyclerView với GridLayoutManager hiển thị Số lần xem thẻ. Tôi muốn các thẻ sắp xếp lại theo kích thước màn hình (ứng dụng Google Play thực hiện loại điều này với thẻ ứng dụng của nó). Dưới đây là một ví dụ:GridLayoutManager - cách tự động điều chỉnh cột phù hợp?

enter image description here

enter image description here

Sau đây là cách ứng dụng của tôi trông vào lúc này:

enter image description here

enter image description here

Như bạn có thể thấy các thẻ chỉ kéo dài và không phù hợp với không gian trống được làm từ định hướng thay đổi tion. Vì vậy, làm thế nào tôi có thể làm điều này?

Code:

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; } 
     } 
    } 
} 

Fragment Layout:

<?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> 

Fragment mục Layout:

<?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

Bạn đã nhận được giải pháp chưa ?? bị mắc kẹt cũng –

+0

Đối với tôi, khi tôi cung cấp chiều rộng thực tế cho recyclerView, các mục của nó tự động. –

Trả lời

49

Bạn có thể tính số cột và tải hình ảnh như được tính toán. Xác định một funtion tĩnh để tính toán như:

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; 
    } 
} 

Và sau đó khi sử dụng nó trong các hoạt động hoặc đoạn bạn có thể làm như thế này:

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

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

Mã tuyệt vời. Hoạt động hoàn hảo cho tôi. khi tôi thay thế 180 bằng 140. – Abhishek

+0

'180' trong câu trả lời là gì? – khateeb

+2

180 là chiều rộng của mục lưới của bạn. Bạn có thể thay đổi nó theo quy ước của bạn. – Ariq

1

Constructor new GridLayoutManager(activity, 2) khoảng GridLayoutManager(Context context, int spanCount) nơi spanCount là số cột trong lưới.

Cách tốt nhất là kiểm tra chiều rộng cửa sổ/chế độ xem và dựa trên chiều rộng này đếm số lượng nhịp bạn muốn hiển thị.

17

Các GridLayoutManager của constructor có một cuộc tranh cãi spanCount đó là

Số cột trong lưới

Bạn có thể khởi tạo các nhà quản lý với một giá trị integer resource và cung cấp giá trị khác nhau cho different screens (tức values-w600 , values-large, values-land).

3

tôi đã cố gắng @Riten câu trả lời và làm việc funtastic !! Nhưng tôi đã không hài lòng với mã hóa cứng "180" Vì vậy, tôi sửa đổi như sau:

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); 
    } 
} 

đâu "viewId" là bố trí được sử dụng như quan điểm trong RecyclerView như trong R.layout.item_for_recycler

Không chắc chắn về tác động của View.inflate vì tôi chỉ sử dụng nó để lấy Chiều rộng, không có gì khác.

Sau đó trên GridLayoutManager tôi làm:

GridLayoutManager gridLayoutManager = new GridLayoutManager(this, Utility.columnQty(this, R.layout.item_for_recycler)); 

CẬP NHẬT: tôi đã thêm nhiều dòng mã như tôi sử dụng nó để có được một khoảng cách chiều rộng tối thiểu trong Grid. Tính khoảng cách:

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

Hi Racu, tôi đã thử phương pháp của bạn, nhưng 'view.getMeasuredWidth()' luôn trả về 0. Bất kỳ ý tưởng nào? – Sam

+0

Không có ý tưởng, tôi chỉ có thể nghĩ rằng có thể là xml, bạn có thể có "0dp". Tôi sẽ cập nhật toàn bộ lớp học, khi tôi thực hiện một số thay đổi. – Racu

+0

@Sam Tôi đã cập nhật lên phiên bản mới nhất của mình, lưu ý rằng tôi cũng sử dụng phiên bản này để đặt khoảng cách giữa các mục. Các "15" ở đó, đảm bảo với tôi rằng các không gian betwenn mục sẽ có ít nhất 15px, nếu nó ít hơn, phương pháp sẽ loại bỏ một cột và tính toán lại khoảng cách. Ước gì tôi có thể tạo ra "15px" "15dp" hoặc một cái gì đó ít mã hóa cứng hơn. GL – Racu

Các vấn đề liên quan