Span-like RecyclerView like Snapchat-Discover

我试图创build一个spannable recyclerView像下面的图片使用TwoWayView库: https : //github.com/lucasr/twoway-view

但是我无法得到所需的视图,并且有空的单元格被排除在外。

final SpannableGridLayoutManager.LayoutParams lp = (SpannableGridLayoutManager.LayoutParams) itemView.getLayoutParams(); //final int span1 = (itemId == 0 || itemId == 3 ? 2 : 1); //final int span2 = (itemId == 0 ? 2 : (itemId == 3 ? 3 : 1)); int span1 = 0; //h int span2 = 0; //w switch(itemId) { case 0: span1 = 2; span2 = 1; break; case 1: span1 = 2; span2 = 1; break; case 2: span1 = 2; span2 = 1; break; case 3: span1 = 3; span2 = 1; break; case 4: span1 = 3; span2 = 1; break; case 5: span1 = 4; span2 = 3; break; case 6: span1 = 2; span2 = 1; break; case 7: span1 = 2; span2 = 1; break; case 8: span1 = 2; span2 = 1; break; case 9: span1 = 2; span2 = 1; break; case 10: span1 = 4; span2 = 3; break; } final int colSpan = span2; final int rowSpan = span1; if (lp.rowSpan != rowSpan || lp.colSpan != colSpan) { lp.rowSpan = rowSpan; lp.colSpan = colSpan; itemView.setLayoutParams(lp); } 

XML:

 <org.lucasr.twowayview.widget.TwoWayView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" style="@style/TwoWayView" app:twowayview_layoutManager="ListLayoutManager"/> 

参考: 在这里输入图像说明

编辑1我能够解决这个问题,通过在XML和以下跨度更改rowWidth 12:

  //PADDING-- left / top / right / bottom switch(itemId%11) { case 0: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 1: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels2, dpAsPixels1, dpAsPixels2, dpAsPixels1); break; case 2: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 3: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 4: span1 = 8; span2 = 14; //holder.cont_frienddetail.setPadding(dpAsPixels2, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 5: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 6: span1 = 6; span2 = 10; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels2, dpAsPixels1); break; case 7: span1 = 6; span2 = 10; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 8: span1 = 8; span2 = 14; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels2, dpAsPixels1); break; case 9: span1 = 4; span2 = 7; ///holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 10: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; } 

Solutions Collecting From Web of "Span-like RecyclerView like Snapchat-Discover"

想象一下,将您的示例图像和每个单元格的垂直边界延伸到整个屏幕。 如果你这样做,你会看到有六列。 这就是解决你的问题的洞察力。

现在你可以使用普通的RecyclerViewGridLayoutManager来完成这个工作,而不必依赖修复GitHub库中的所有bug。

创build一个6列的GridLayoutManager ,然后指定一个自定义的SpanSizeLookup ,它将使你的单元格宽度不同。

在您的示例图像中,第一行中的三个单元格将每个跨越2列。 第二行中的两个较大的单元格各自跨越三列。 左下angular的单元格跨4列。

所以现在你所要做的就是创build一个SpanSizeLookup来设置正确的宽度。

  // Create a grid layout with 6 columns // (least common multiple of 2 and 3) GridLayoutManager layoutManager = new GridLayoutManager(this, 6); layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { int widthClass = determineWidth(position) // TODO here you figure out what the cell width should be switch (widthClass) { case SMALL: return 2; // fits 3 per row case MEDIUM: return 3; // fits 2 per row case LARGE: // fits 2 per row, one LARGE and one SMALL return 4; default: throw new IllegalStateException("don\'t know about widthClass " + widthClass); } } }); 

你可以在这里查看我的原始答案到一个类似的问题。

我能够通过在XML和下面的跨度中将rowWidth更改为12来解决此问题:

  //PADDING-- left / top / right / bottom switch(itemId%11) { case 0: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 1: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels2, dpAsPixels1, dpAsPixels2, dpAsPixels1); break; case 2: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 3: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 4: span1 = 8; span2 = 14; //holder.cont_frienddetail.setPadding(dpAsPixels2, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 5: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 6: span1 = 6; span2 = 10; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels2, dpAsPixels1); break; case 7: span1 = 6; span2 = 10; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 8: span1 = 8; span2 = 14; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels2, dpAsPixels1); break; case 9: span1 = 4; span2 = 7; ///holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; case 10: span1 = 4; span2 = 7; //holder.cont_frienddetail.setPadding(dpAsPixels0, dpAsPixels1, dpAsPixels0, dpAsPixels1); break; } 

看到这个链接你获得帮助的链接

完整的源代码

在这里输入图像说明

码。

MainActivity.java

 import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends AppCompatActivity { public static final String TYPE_VERTICAL_LIST= "Vertical List"; public static final String TYPE_HORIZONTAL_LIST= "Horizontal List"; public static final String TYPE_GRID_VIEW = "Grid View"; public static final String TYPE_HORIZONTAL_GRID_VIEW_STAGGERED = "HorizontalStaggeredGridView"; public static final String TYPE_VERTICAL_GRID_VIEW_STAGGERED = "VerticalStaggeredGridView"; public static final String TYPE = "TYPE"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); attachFragment(TYPE_VERTICAL_LIST); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); /** Return false so that we can capture {@link PlaceholderFragment#onOptionsItemSelected(android.view.MenuItem)} events in {@link PlaceholderFragment} Fragment*/ return false; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.action_vertical_list: attachFragment(TYPE_VERTICAL_LIST); break; case R.id.action_horizontal_list: attachFragment(TYPE_HORIZONTAL_LIST); break; case R.id.action_grid_view: attachFragment(TYPE_GRID_VIEW); break; case R.id.action_vertical_grid_view_staggered: attachFragment(TYPE_VERTICAL_GRID_VIEW_STAGGERED); break; case R.id.action_horizontal_grid_view_staggered: attachFragment(TYPE_HORIZONTAL_GRID_VIEW_STAGGERED); break; } return super.onOptionsItemSelected(item); } /** Attach placeholder fragment here and show different kind of RecyclerView flavours eg List or grid based on user selection in the options menu.*/ private void attachFragment(String type){ Bundle bundle = new Bundle(); bundle.putString(TYPE, type); PlaceholderFragment placeholderFragment = new PlaceholderFragment(); placeholderFragment.setArguments(bundle); getSupportFragmentManager().beginTransaction().replace(R.id.container, placeholderFragment).commit(); } } 

PlaceholderFragment.java

 import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; public class PlaceholderFragment extends Fragment { private static final String TAG = "PlaceholderFragment"; /*Number of columns in the grid view*/ private static final int NUM_OF_COLUMNS = 2; /*Total number of items in the RecyclerView*/ private static final int NUM_OF_ITEMS = 100; private ArrayList<DataModel> mDataModels; private RecyclerView recyclerView; private RecyclerViewAdapter recyclerViewAdapter; private int addItemCount = 0; public PlaceholderFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { //Must be set in order to capture menu item click events. If you don't set it, it will not show the menu items set in the Activity holding this fragment. setHasOptionsMenu(true); View rootView = inflater.inflate(R.layout.fragment_main, container, false); recyclerView = (RecyclerView)rootView.findViewById(R.id.recyclerview); RecyclerView.LayoutManager layoutManager = null; String type = getArguments().getString(MainActivity.TYPE); if( type.equals(MainActivity.TYPE_VERTICAL_LIST)){ /*LinearLayoutManager to show a vertical list view*/ layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false); }else if(type.equals(MainActivity.TYPE_HORIZONTAL_LIST)){ /*LinearLayoutManager to show a horizontal list view*/ layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false); }else if(type.equals(MainActivity.TYPE_GRID_VIEW)){ /*LinearLayoutManager to show a grid view. We can specify number of columns in the grid.*/ layoutManager = new GridLayoutManager(getActivity(), NUM_OF_COLUMNS); }else if(type.equals(MainActivity.TYPE_HORIZONTAL_GRID_VIEW_STAGGERED)){ /*LinearLayoutManager to show a staggered grid view. We can specify number of columns in the grid.*/ //spanCount: If orientation is vertical, spanCount is number of columns. If orientation is horizontal, spanCount is number of rows. //orientation: StaggeredGridLayoutManager.HORIZONTAL or StaggeredGridLayoutManager.HORIZONTAL layoutManager = new StaggeredGridLayoutManager(3/*span count*/, StaggeredGridLayoutManager.HORIZONTAL/* orientation*/); }else if(type.equals(MainActivity.TYPE_VERTICAL_GRID_VIEW_STAGGERED)){ /*LinearLayoutManager to show a staggered grid view. We can specify number of columns in the grid.*/ //spanCount: If orientation is vertical, spanCount is number of columns. If orientation is horizontal, spanCount is number of rows. //orientation: StaggeredGridLayoutManager.HORIZONTAL or StaggeredGridLayoutManager.HORIZONTAL layoutManager = new StaggeredGridLayoutManager(2/*span count*/, StaggeredGridLayoutManager.VERTICAL/* orientation*/); } recyclerView.setLayoutManager(layoutManager); mDataModels = getDataModelList(); recyclerViewAdapter = new RecyclerViewAdapter(mDataModels, type); /*Third party ItemDecoration found from https://gist.github.com/alexfu/0f464fc3742f134ccd1e*/ RecyclerView.ItemDecoration verticalDivider = new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST); RecyclerView.ItemDecoration horizontalDivider = new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST); recyclerView.addItemDecoration(horizontalDivider); recyclerView.addItemDecoration(verticalDivider); // this is the default; // this call is actually only necessary with custom ItemAnimators recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(recyclerViewAdapter); return rootView; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.action_add_item: addItem(); break; case R.id.action_delete_item: deleteItem(); break; } return super.onOptionsItemSelected(item); } /** Creates and returns the data items to be shown in the Recycler View*/ private ArrayList<DataModel> getDataModelList(){ ArrayList<DataModel> dataModels = new ArrayList<>(); for (int i = 0; i < NUM_OF_ITEMS; i++) { dataModels.add(new DataModel("Title:"+i)); } return dataModels; } /** Adds a single item in the existing list*/ private final void addItem(){ //Adding item at top second position(ie at index 1). mDataModels.set(1, new DataModel("Added Item "+String.valueOf(++addItemCount))); //See for similar methods to know more item insertion options recyclerViewAdapter.notifyItemInserted(1); } /** Deletes a single item from the existing list*/ private void deleteItem(){ //Removing top item mDataModels.remove(0); //See for similar methods to know more item removing options recyclerViewAdapter.notifyItemRemoved(0); } } 

ListItemViewHolder.java

 public class ListItemViewHolder extends ViewHolder { ImageView imgView; TextView title; public ListItemViewHolder(View itemView) { super(itemView); title = (TextView) itemView.findViewById(R.id.textview_title); imgView = (ImageView) itemView.findViewById(R.id.imageview_icon); } } 

DividerItemDecoration.java

  import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; public class DividerItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } } } 

RecyclerViewAdapter.java

 import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; public class RecyclerViewAdapter extends RecyclerView.Adapter<ListItemViewHolder> { private ArrayList<DataModel> mDataModels; private String mType; public RecyclerViewAdapter(ArrayList<DataModel> mDataModels, String mType) { this.mDataModels = mDataModels; this.mType = mType; } @Override public ListItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler_view, parent, false); return new ListItemViewHolder(itemView); } @Override public void onBindViewHolder(ListItemViewHolder holder, int position) { DataModel model = mDataModels.get(position); holder.title.setText(model.getTitle()); if(position % 2 == 0){ if(mType.equals(MainActivity.TYPE_VERTICAL_GRID_VIEW_STAGGERED)){ holder.imgView.setImageResource(R.drawable.lollipop); }else{ holder.imgView.setImageResource(R.drawable.lollipop_h1); } }else if(position % 3 == 0){ if(mType.equals(MainActivity.TYPE_VERTICAL_GRID_VIEW_STAGGERED)){ holder.imgView.setImageResource(R.drawable.lollipop1); }else{ holder.imgView.setImageResource(R.drawable.lollipop_h2); } }else{ if(mType.equals(MainActivity.TYPE_VERTICAL_GRID_VIEW_STAGGERED)){ holder.imgView.setImageResource(R.drawable.lollipop2); }else{ holder.imgView.setImageResource(R.drawable.lollipop_h3); } } } @Override public int getItemCount() { return mDataModels.size(); } } 

DataModel.java

 public class DataModel { private String title; public DataModel(String title) { this.title = title; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } 

activity_main.xml中

  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" tools:ignore="MergeRootFrame" /> 

fragment_main.xml

  <android.support.v7.widget.RecyclerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" tools:listitem="@layout/item_recycler_view" /> </RelativeLayout> 

item_recycler_view.xml

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:padding="8dp" android:background="@android:color/holo_green_dark"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageview_icon" android:src="@drawable/lollipop" android:scaleType="centerCrop"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="New Text" android:id="@+id/textview_title" android:background="@android:color/darker_gray"/> </LinearLayout>