Intereting Posts
Как добавить эффект градиента в фоновый цвет TextView в ListView? Обнаруживать, когда фрагмент становится видимым Android installLocation при построении с cordova cli (Android) Как показать часть изображения? Android Как правильно получить значение с коммутатора? Как играть в видео как живые обои android? Изменение цвета фона активированного списка элементов на сотах Android: EditText теряет содержимое при прокрутке в ListView? Платеж в приложении не работает (ваш запрос не может быть обработан) Несоответствие типов: невозможно преобразовать из StringBuilder в String Android: автоматически показывать мягкую клавиатуру, когда фокус находится на EditText Создание проекта Android с муравьем, имеющим проект библиотеки в зависимости от другого проекта библиотеки Gradle: 'buildTypes' нельзя применить к groovy.lang.Closure API Карт Google v2: LatLngBounds от CameraPosition Как воспроизводить видео в VideoView внутри ViewPager с сервера

Динамическая установка фиксированной высоты для просмотра сетчатой ​​сетки

Я пытаюсь взять RecyclerView с StaggeredGridLayout и сделать его фиксированной высотой, если он измеряет представления и динамически устанавливает высоту. Я переопределяю onMeasure() , но это не всегда кажется правильным. Я бы сказал, что это работает примерно в 50% случаев. Другие 50% времени, которое оно измеряет. Я думаю, это связано с тем, когда текст обертывается в view_tile_small.xml , но я не уверен.

Фрагмент

 public class AtTheMuseumFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener{ //Odds n Ends Variables private MapFragment mapFragment; private FragmentTransaction fragmentTransaction; private User user = new User(); private MuseumCollection museumCollection; private Context context; //Story Grid Adapter Variables private AtTheMuseumAdapter nearMeAdapter; private TileFactory tileFactory; //Interfaces private OnFragmentChangeListener changeListener; @Bind(R.id.stories_list_view) RecyclerView storiesListView; @Bind(R.id.swipe_container) SwipeRefreshLayout swipeRefreshLayout; public static AtTheMuseumFragment newInstance(MuseumCollection museumCollection) { AtTheMuseumFragment fragment = new AtTheMuseumFragment(); Bundle args = new Bundle(); fragment.setArguments(args); return fragment; } public AtTheMuseumFragment() { // Required empty public constructor } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); museumCollection = ((MainActivity) getActivity()).getMuseumCollection(); context = getActivity().getApplicationContext(); tileFactory = new TileFactory(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_museum, container, false); ButterKnife.bind(this, view); //Sets up the layoutManager to the Mason View storiesListView.setLayoutManager(new MeasuredStaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); //Sets up The map try{ fragmentTransaction = getChildFragmentManager().beginTransaction(); mapFragment = MapFragment.newInstance(MapFragment.MUSEUM); fragmentTransaction.add(R.id.museum_map, mapFragment).commit(); }catch (Exception e){ Log.d("Map - Initial Inflate:", e.getMessage()); } //Sets up the swipe to refresh jawn swipeRefreshLayout.setOnRefreshListener(this); setNearMeAdapter(); return view; } @Override public void onResume(){ super.onResume(); } @Override public void onAttach(Activity activity) { super.onAttach(activity); try { changeListener = (OnFragmentChangeListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener"); } } @Override public void onDetach() { super.onDetach(); changeListener = null; } /** * Loads the adapter once the data is ready * */ public void setNearMeAdapter(){ List<MuseumObject> newList = museumCollection.getObjectsNearUser(User.position, 4); List<Tile> tiles = tileFactory.createAtMuseumFeed(newList, true); nearMeAdapter = new AtTheMuseumAdapter(context, tiles, getActivity()); storiesListView.setAdapter(nearMeAdapter); } /** * Refreshes Adapter with new data * */ public void refreshNearMeAdapter(){ //TODO CHANGE THIS TO LOCATION BASED WHEN TIME IS RIGHT - Peter //nearMeAdapter.setNewData(MuseumCollection.getObjectsNearUser(User.position, 4)); List<MuseumObject> newList = museumCollection.getRandomOrder(); nearMeAdapter.setNewData(tileFactory.createAtMuseumFeed(newList,false)); } /** * Adds past data to the Adapter * */ public void loadPastObjects(){ //TODO MAKE THIS NOT ONLY LOAD RANDOM DATA - Peter List<MuseumObject> newList = museumCollection.getRandomOrder(); nearMeAdapter.addNewData(tileFactory.createAtMuseumFeed(newList, false)); nearMeAdapter.notifyDataSetChanged(); } @Override public void onRefresh() { user.updateUserLocation(); refreshNearMeAdapter(); mapFragment.refreshMap(museumCollection.getObjectFeed()); swipeRefreshLayout.setRefreshing(false); } public interface OnFragmentChangeListener { void onFragmentChange(String fragment); } @OnClick(R.id.explore_map) public void exploreMap(){ changeListener.onFragmentChange("map"); } @OnClick(R.id.load_more) public void loadMore(){ loadPastObjects(); } } 

MeasuredStaggeredGridLayoutManager

 public class MeasuredStaggeredGridLayoutManager extends StaggeredGridLayoutManager { public MeasuredStaggeredGridLayoutManager(int spanCount, int orientation) { super(spanCount, orientation); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; int heightR = 0; int heightL = 0; for (int i = 0; i < getItemCount(); i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); if (getOrientation() == HORIZONTAL) { width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { if(i % 2 == 0){ heightL += mMeasuredDimension[1]; }else{ heightR += mMeasuredDimension[1]; } if (i == 0) { width = mMeasuredDimension[0]; } } } switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } if(heightL != 0 || heightR != 0){ height = (heightL > heightR) ? heightL : heightR; } //TODO come up with a better way to fix the slightly wrong height // must be not accounting for padding or margin or something - Peter height += (20 * (getItemCount() / 2)) + 5; setMeasuredDimension(width, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } } 

AtTheMuseumAdapter

 public class AtTheMuseumAdapter extends RecyclerView.Adapter<AtTheMuseumAdapter.MuseumStoriesViewHolder> { private List<Tile> tiles; private LayoutInflater inflater; private AdapterCallback mListener; private Context context; public AtTheMuseumAdapter(Context context, List<Tile> tiles, Activity activity) { this.tiles = tiles; this.context = context; inflater = LayoutInflater.from(this.context); //Sets up interface between Stock Adapter and Fragment try { this.mListener = ((AdapterCallback) activity); } catch (ClassCastException e) { throw new ClassCastException("Fragment must implement AdapterCallback."); } } @Override public MuseumStoriesViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = inflater.inflate(R.layout.view_tile_small, viewGroup, false); MuseumStoriesViewHolder holder = new MuseumStoriesViewHolder(view); return holder; } @Override public void onBindViewHolder(MuseumStoriesViewHolder holder, int position) { Tile currentTile = tiles.get(position); holder.title.setText(currentTile.getTitle()); holder.desc.setText(currentTile.getDescription()); holder.type.setText(currentTile.getObjectTypeName()); //Using Picasso since it handles caching and all that jazz Picasso.with(context) .load(currentTile.getImg()) .into(holder.img); } @Override public int getItemCount() { return tiles.size(); } public void setNewData(List<Tile> newItems){ tiles = newItems; notifyDataSetChanged(); } public void addNewData(final List<Tile> newItems){ tiles.addAll(newItems); notifyDataSetChanged(); } class MuseumStoriesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public TextView type,title,desc; public ImageView img; public MuseumStoriesViewHolder(View itemView) { super(itemView); //Tried Butterknife, but it doesn't seem like it was working in the view holder. - Peter type = (TextView) itemView.findViewById(R.id.small_box_type); title = (TextView) itemView.findViewById(R.id.small_box_title); desc = (TextView) itemView.findViewById(R.id.small_box_desc); img = (ImageView) itemView.findViewById(R.id.small_box_image); itemView.setOnClickListener(this); } @Override public void onClick(View view) { Tile t = tiles.get(getPosition()); switch (t.getObjectTypeName()){ case Tile.OBJECT_NAME: mListener.onObjectClick(t.getObjectID()); break; case Tile.TOUR_NAME: mListener.onTourCLick(t.getTourID()); break; case Tile.STORY_NAME: mListener.onStoryClick(t.getObjectID(), t.getStoryID()); break; } } } public interface AdapterCallback { public void onObjectClick(String objectID); public void onStoryClick(String objectID, String storyID); public void onTourCLick(String tourID); } } 

view_tile_small.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="wrap_content" android:layout_margin="@dimen/margin_small" android:background="@color/small_box_background_color"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <!--TODO Make layout_height wrap contenet --> <ImageView android:id="@+id/small_box_image" android:layout_width="match_parent" android:layout_height="150dp" android:adjustViewBounds="true" android:scaleType="centerCrop" android:maxHeight="150dp" android:background="@color/transparent"/> <ImageView android:layout_width="35dp" android:layout_height="35dp" android:layout_gravity="right" android:src="@drawable/abc_btn_rating_star_off_mtrl_alpha" /> <TextView android:id="@+id/small_box_type" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/margin_normal" android:paddingBottom="@dimen/margin_normal" android:textSize="@dimen/font_small" android:textColor="@color/font_white" android:background="@drawable/small_box_text_bottom_border" android:layout_gravity="bottom" android:text="Object Story" /> </FrameLayout> <TextView android:id="@+id/small_box_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/margin_larger" android:layout_marginBottom="@dimen/margin_normal" android:layout_marginRight="@dimen/margin_larger" android:layout_marginTop="@dimen/margin_larger" android:textSize="@dimen/font_large" android:textColor="@color/font_black" android:text="Sample Text Here" /> <TextView android:id="@+id/small_box_desc" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/margin_larger" android:layout_marginBottom="@dimen/margin_larger" android:textSize="@dimen/font_normal" android:textColor="@color/font_black" android:textStyle="italic" android:text="Sample Text Here" /> </LinearLayout> 

fragment_museum

 <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context="com.bluecadet.android.nasm.ui.AtTheMuseumFragment" android:id="@+id/swipe_container" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="100dp"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF" android:descendantFocusability="blocksDescendants" > <TextView android:id="@+id/museum_header" style="@style/header" android:text="@string/museum_header" android:layout_margin="@dimen/margin_larger" android:elevation="8dp" /> <FrameLayout android:layout_width="match_parent" android:layout_height="275dp" android:elevation="2dp" > <FrameLayout android:id="@+id/museum_map" android:layout_height="fill_parent" android:layout_width="match_parent" /> <include layout="@layout/view_explore_map" /> </FrameLayout> <android.support.v7.widget.RecyclerView android:id="@+id/stories_list_view" xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/margin_normal" android:layout_marginLeft="@dimen/margin_small" android:layout_marginRight="@dimen/margin_small" android:layout_marginTop="@dimen/margin_normal" android:stretchMode="columnWidth" /> <Button android:id="@+id/load_more" style="@style/home_button" android:gravity="center" android:text="@string/button_load_more" /> </LinearLayout> </ScrollView> </android.support.v4.widget.SwipeRefreshLayout> 

Код ViewTreeObserver, с которым я сейчас играю. Это во Фрагменте.

  mTopStoriesListView.setLayoutManager(new NewMeasuredStaggeredLayoutManager(2, StaggeredGridLayoutManager.VERTICAL, mTopStoriesListView)); mTopStoriesListView.setNestedScrollingEnabled(false); //Testing Issue 54 final ViewTreeObserver viewTreeObserver = mTopStoriesListView.getViewTreeObserver(); if (viewTreeObserver.isAlive()) { viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mTopStoriesListView.getViewTreeObserver().removeOnGlobalLayoutListener(this); int l = 0,r = 0; for(int i = 0 ; i < mNearMeAdapter.getItemCount(); i++){ int h = mTopStoriesListView.getLayoutManager().findViewByPosition(i).getHeight(); ViewGroup.MarginLayoutParams layout = (ViewGroup.MarginLayoutParams) mTopStoriesListView.getLayoutManager() .findViewByPosition(i).getLayoutParams(); int t = layout.topMargin; int b = layout.bottomMargin; if(i % 2 == 0){ l += h + t + b; }else{ r += h + t + b; } } int viewHeight = (l > r) ? l : r; mTopStoriesListView.getLayoutParams().height = viewHeight; Log.d("TAG", String.valueOf(viewHeight)); } }); } //END TEST 

Solutions Collecting From Web of "Динамическая установка фиксированной высоты для просмотра сетчатой ​​сетки"

Вы смотрите на ViewTreeObserver?

Я получил аналогичную проблему в проекте, и я нашел его более надежным, чем onMesure, для динамического получения свойств Layout

Вы можете просмотреть его здесь: http://developer.android.com/reference/android/view/ViewTreeObserver.html