2015-10-21 23 views
18

Sto provando a prendere uno RecyclerView con un StaggeredGridLayout e renderlo a un'altezza fissa misurando le viste e impostando l'altezza dinamicamente. Sto ignorando lo onMeasure(), ma non sembra sempre che misuri correttamente. Direi che funziona circa il 50% delle volte. L'altro 50% delle volte in cui lo misura. Penso che abbia a che fare quando il testo viene inserito nello view_tile_small.xml, ma non ne sono sicuro.Impostazione dinamica di un'altezza fissa per una vista a griglia sfalsata

Frammento

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> 

codice ViewTreeObserver sto giocando con la società. È in frammento.

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

Forse hai alcuna soluzione per questo ? Ho lo stesso problema –

+0

No, non l'ho fatto. Ho intenzione di provare la soluzione qui sotto, ma il bug è stato messo in secondo piano per il momento a causa di storie più importanti. Dovrei provare di nuovo a risolverlo domani. –

+0

Ok cool mal provate a farlo funzionare e postare una soluzione se lo aggiusto .. apprezzerei se si fa lo stesso se capita;) –

risposta

5

Hai visto ViewTreeObserver?

ho avuto un problema simile su un progetto passato e l'ho trovato più affidabile di onMesure per ottenere dinamicamente le proprietà di layout

Si può passare attraverso da qui: http://developer.android.com/reference/android/view/ViewTreeObserver.html

+0

No, non l'ho ancora esaminato. Domani darò un'occhiata a questo. Grazie per l'aiuto! –

+0

Sono stato finalmente in grado di tornare a questo problema ... Quindi, come utilizzerei 'ViewTreeObserver' per misurare ogni vista all'interno di' RecyclerView'? Vedo esempi di misurazione di una singola vista, ma non di viste dall'interno di un adattatore. –

Problemi correlati