动态设置交错网格视图的固定高度 [英] Dynamically Setting a Fixed Height for a Staggered Grid View

查看:97
本文介绍了动态设置交错网格视图的固定高度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将RecyclerViewStaggeredGridLayout结合使用,并通过测量视图并动态设置高度来将其设置为固定高度.我覆盖了onMeasure(),但是它似乎并不总是能够正确测量.我会说它大约有50%的时间有效.其他50%的时间都对其进行了测量.我认为这与view_tile_small.xml中的文字换行有关,但我不确定.

I'm trying to take a RecyclerView with a StaggeredGridLayout and make it a fixed height by having it measure the views and set the height dynamically. I'm overriding the onMeasure(), but it does not always seem to measure correctly. I'd say it works about 50% of the time. The other 50% of the time it under measures it. I think it has to do with when the text wraps in the view_tile_small.xml, but I'm not sure.

片段

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

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

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

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>

碎片博物馆

<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代码.在片段中.

ViewTreeObserver code I'm playing with now. It's 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

推荐答案

您看过ViewTreeObserver吗?

Have you look at ViewTreeObserver ?

我在通过的项目中遇到了类似的问题,并且发现它比onMesure更可靠地动态获取Layout属性

I got a similar problem on a passed project and I have found it more reliable than onMesure to dynamically get Layout properties

您可以从这里进行遍历: http://developer.android. com/reference/android/view/ViewTreeObserver.html

You can go through it from here : http://developer.android.com/reference/android/view/ViewTreeObserver.html

这篇关于动态设置交错网格视图的固定高度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆