2015-11-06 1229 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