2015-10-21 6 views
18

Ich versuche, eine RecyclerView mit einer StaggeredGridLayout zu nehmen und sie zu einer festen Höhe zu machen, indem sie die Ansichten misst und die Höhe dynamisch einstellt. Ich überschreibe die onMeasure(), aber es scheint nicht immer korrekt zu messen. Ich würde sagen, dass es ungefähr 50% der Zeit funktioniert. Die anderen 50% der Zeit es untermisst es. Ich denke, es hat damit zu tun, wenn der Text in view_tile_small.xml verpackt, aber ich bin mir nicht sicher.Dynamische Einstellung einer festen Höhe für eine gestaffelte Rasteransicht

Fragment

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 code Ich bin jetzt zu spielen. Es ist in Fragment.

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

Bitte benutzen Sie hierfür keine Lösung bekam ? Ich habe das gleiche Problem –

+0

Nein, habe ich nicht. Ich wollte die untenstehende Lösung ausprobieren, aber der Bug wurde vorerst wegen wichtigerer Geschichten auf Eis gelegt. Ich sollte versuchen, es morgen wieder zu lösen. –

+0

Ok cool krank versuchen, es zu arbeiten und eine Lösung zu veröffentlichen, wenn ich es beheben .. würde mich freuen, wenn Sie das gleiche tun, wenn Sie es bekommen;) –

Antwort

5

Haben Sie sich ViewTreeObserver angesehen?

habe ich ein ähnliches Problem auf einem weitergegeben Projekt und ich habe es zuverlässiger gefunden als onMesure dynamisch Eigenschaften Layout erhalten

Sie, wenn Sie es von hier aus gehen kann: http://developer.android.com/reference/android/view/ViewTreeObserver.html

+0

Nein, ich habe das nicht untersucht. Ich werde es heute später anschauen. Danke für die Hilfe! –

+0

Endlich konnte ich auf dieses Problem zurückkommen ... Also, wie würde ich 'ViewTreeObserver' verwenden, um die einzelnen Ansichten innerhalb des' RecyclerView' zu messen? Ich sehe Beispiele zum Messen einer einzelnen Ansicht, aber keine Ansichten von innerhalb eines Adapters. –