【问题标题】:How can I make a horizontal ListView in Android? [duplicate]如何在 Android 中制作水平 ListView? [复制]
【发布时间】:2011-04-22 01:55:12
【问题描述】:

可能重复:
Horizontal ListView in Android?

就像 Android 中的许多事情一样,您不会认为这是一个如此困难的问题,但是哦,天哪,您会错吗?而且,就像 Android 中的许多东西一样,API 甚至没有提供一个可合理扩展的起点。如果我要推出自己的 ListView,我会被诅咒的,而我想要的只是拿走东西并把它翻过来。 \咆哮

好了,说完了,让我们谈谈问题本身。我需要的基本上与Gallery 完全一样,但没有中心锁定功能。我真的不需要ListView 的listSelector,但它很不错。大多数情况下,我可以在ScrollView 中使用LinearLayout 来做我想做的事,但我需要来自ListAdapter 的子视图,我真的很想拥有一个视图回收器。而且我真的不想写任何布局代码。

我查看了其中一些类的源代码...

Gallery:如果我重写大多数“onXyz”方法,复制它们的所有源代码,但不要调用scrollIntoSlots(),看起来我可以使用Gallery。但我敢肯定,如果我这样做,我会遇到一些无法访问的成员字段或其他无法预料的后果。

AbsSpinner:由于mRecycler 字段是包私有的,我怀疑我能否扩展这个类。

AbsListView: 看起来这个类只用于垂直滚动,所以没有帮助。

AdapterView:我从来不需要直接扩展这个类。如果你告诉我这很容易做到,而且很容易推出我自己的RecycleBin,我会非常怀疑,但我会试一试。

我想我可以复制 both AbsSpinnerGallery 来获得我想要的东西......希望这些类没有使用一些我无法访问的包私有变量。大家觉得这是个好习惯吗?有没有人有任何教程或第三方解决方案可以让我朝着正确的方向前进?

更新:
到目前为止,我发现的唯一解决方案就是自己做。自从提出这个问题以来,我已经覆盖了AdapterView 并从头开始实现了我自己的“Horizo​​ntalListView”。真正覆盖 Gallery 的中心锁定功能的唯一方法是覆盖私有 scrollIntoSlots 方法,我认为这需要在运行时生成一个子类。如果你有足够的胆量这样做,这可以说是最好的解决方案,但我不想依赖可能会改变的无证方法。

下面的 Swathi EP 建议我给 Gallery 一个 OnTouchListener 并覆盖滚动功能。如果您不关心列表中是否有投掷支持,或者如果视图在投掷动画结束时捕捉到中心是可以的,那么这个为你工作!然而,最终仍然证明在不移除弹射支撑的情况下移除中心锁定功能是不可能的。我问你,什么样的清单不会扔?

所以,唉,这对我不起作用。 :-( 但是,如果您对这种方法感兴趣,请继续阅读...

我还必须在 Swathi 的代码中添加一些内容才能得到我想要的。在GestureListener.onTouch 中,除了委托给手势检测器之外,我还必须为ACTION_UPACTION_CANCEL 事件返回true。这成功地禁用了中心锁定功能,但它也禁用了投掷。通过将我自己的 GestureListener 委托给 Gallery 的 onFling 方法,我能够重新启用投掷。如果您想尝试一下,请进入您的 ApiDemos 示例代码并将 Gallery1.java 类替换为以下代码:

import com.example.android.apis.R;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.View.OnTouchListener;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;

public class Gallery1 extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.gallery_1);

        // Reference the Gallery view
        final Gallery g = (Gallery) findViewById(R.id.gallery);

        // Set the adapter to our custom adapter (below)
        g.setAdapter(new ImageAdapter(this));

        // Set a item click listener, and just Toast the clicked position
        g.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView parent, View v, int position, long id) {
                Toast.makeText(Gallery1.this, "" + position, Toast.LENGTH_SHORT).show();
            }
        });

        // Gesture detection
        final GestureDetector gestureDetector = new GestureDetector(new MyGestureDetector(g));
        OnTouchListener gestureListener = new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                boolean retVal = gestureDetector.onTouchEvent(event);
                int action = event.getAction();
                if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
                    retVal = true;
                    onUp();
                }
                return retVal;
            }

            public void onUp() {
                // Here I am merely copying the Gallery's onUp() method.
                for (int i = g.getChildCount() - 1; i >= 0; i--) {
                    g.getChildAt(i).setPressed(false);
                }
                g.setPressed(false);
            }
        };
        g.setOnTouchListener(gestureListener);

        // We also want to show context menu for longpressed items in the gallery
        registerForContextMenu(g);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        menu.add(R.string.gallery_2_text);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        Toast.makeText(this, "Longpress: " + info.position, Toast.LENGTH_SHORT).show();
        return true;
    }

    public class ImageAdapter extends BaseAdapter {
        int mGalleryItemBackground;

        public ImageAdapter(Context c) {
            mContext = c;
            // See res/values/attrs.xml for the <declare-styleable> that defines
            // Gallery1.
            TypedArray a = obtainStyledAttributes(R.styleable.Gallery1);
            mGalleryItemBackground = a.getResourceId(
                    R.styleable.Gallery1_android_galleryItemBackground, 0);
            a.recycle();
        }

        public int getCount() {
            return mImageIds.length;
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView i = new ImageView(mContext);

            i.setImageResource(mImageIds[position]);
            i.setScaleType(ImageView.ScaleType.FIT_XY);
            i.setLayoutParams(new Gallery.LayoutParams(136, 88));

            // The preferred Gallery item background
            i.setBackgroundResource(mGalleryItemBackground);

            return i;
        }

        private Context mContext;

        private Integer[] mImageIds = {
                R.drawable.gallery_photo_1,
                R.drawable.gallery_photo_2,
                R.drawable.gallery_photo_3,
                R.drawable.gallery_photo_4,
                R.drawable.gallery_photo_5,
                R.drawable.gallery_photo_6,
                R.drawable.gallery_photo_7,
                R.drawable.gallery_photo_8
        };
    }

    public class MyGestureDetector extends SimpleOnGestureListener {

        private Gallery gallery;

        public MyGestureDetector(Gallery gallery) {
            this.gallery = gallery;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 
                float velocityY) {
            return gallery.onFling(e1, e2, velocityX, velocityY);
        }
    }

}

【问题讨论】:

    标签: android listview gallery


    【解决方案1】:

    看完这篇文章,我实现了自己的水平ListView。你可以在这里找到它:http://dev-smart.com/horizontal-listview/ 如果这有帮助,请告诉我。

    【讨论】:

    • 这个小部件不尊重 wrap_content 布局参数。如果你不对它的高度进行硬编码,它会让你花一整天的时间来弄清楚为什么它下面的视图都没有显示出来,因为它会吞噬所有的屏幕空间。
    • 我在 layout_height 属性中添加了对“wrap_content”的支持。您可以在 Horizo​​ntalViewList 的分叉版本中找到此修复:github.com/inazaruk/android-dev-smart-lib
    • 我找不到生成jar文件的源代码。 github 的链接发生了一些变化:“在github.com/dinocore1/Android-Horizontal-ListView 开始了新的 github 项目”谁能指出我的源代码或 tha jar 文件本身?
    • Paul,她的链接失效了,可能是替代方案?
    【解决方案2】:

    您是否考虑过使用HorizontalScrollView 来包装您的列表项?这将使您的每个列表项都可以水平滚动(您放在那里的内容取决于您,并且可以使它们成为类似于 ListView 的动态项)。如果您只关注单行项目,这将很有效。

    【讨论】:

    • 我不采用这种方法的两个原因:(1)它没有扩展AdapterView,所以我不能将它与我的ListViewListAdapter 代码互换使用, (2)没有观点的回收。也许可以将此功能添加到滚动视图上,但在我看来,它会扰乱滚动行为,而且它的工作量几乎与简单地扩展 AdapterView 一样多。
    【解决方案3】:

    您知道,可能可以使用现有的 ListView 并明智地覆盖 dispatchDraw()(将画布旋转 90 度)、onTouch()(交换 X 和 Y的 MotionEvent 坐标),也许是 onMeasure() 或其他任何东西来愚弄它认为它是 y by x 而不是 x by y...

    我不知道这是否真的有效,但发现会很有趣。 :)

    【讨论】:

    • 哈哈,我喜欢你的态度……有点不敢尝试……我先试试 Swathi EP 的建议。
    • 所以,我继续实现了我自己的AdapterView 子类,因为我认为这将是一样多的工作和更少的风险。不过,尝试您的建议将是一个有趣的实验。 :-)
    • 额外的好处是该技术可以适用于任何视图类型。难的是找到实际应用! :)
    • 经过调查,只要您的解决方案不需要从使用 mLeft 或任何 m[property] 的子类中的超类重新实现代码,您可能会很幸运.您无法从您自己的自定义类中访问这些属性,这使得几乎所有解决方案都是不可能的。
    【解决方案4】:

    这可能是一个很晚的回复,但它对我们有用。我们使用的是Android提供的同一个图库,只是我们调整了左边距,使屏幕左端被认为是图库的中心。这对我们来说真的很有效。

    【讨论】:

    • 自发布以来,Gallery 已被 API 中的 Horizo​​ntalScrollView 弃用并取代。只需在其上添加一个 LinearLayout,您现在就可以通过代码添加项目。效果很好,虽然没有回收大量物品。
    【解决方案5】:

    我使用了Pauls(见他的answer)Horizo​​ntalListview 的实现,它可以工作,非常感谢你的分享!

    我稍微更改了他的 Horizo​​ntalListView-Class(顺便说一句,Paul 你的类名中有错字,你的类名是“Horizo​​ntialListView”而不是“Horizo​​ntalListView”,“i”太多了)以在选择时更新子视图。

    更新: 我想我在这里发布的代码是错误的,因为我在选择时遇到了问题(我认为这与视图回收有关),我必须回到绘图板...

    更新 2: 好的问题解决了,我只是评论了“removeNonVisibleItems(dx);”在“onLayout(..)”中,我想这会损害性能,但由于我只使用非常小的列表,这对我来说没问题。

    我基本上使用了本教程here on developerlife,只是用 Pauls Horizo​​ntalListView 替换了 ListView,并进行了更改以允许“永久”选择(单击的子项会更改其外观,再次单击时会更改它背部)。

    我是初学者,所以代码中可能有很多丑陋的东西,如果您需要更多详细信息,请告诉我。

    【讨论】:

    • 很高兴他让它工作了,但它不会回收视图,数据是静态的,项目的顺序不能保证,而且在不知道你到底是什么的情况下很难调整/修复'正在这样做是因为其中缺少 cmets。如果您确切地知道自己在做什么,那么您一开始就不需要这个解决方案。伙计,我希望 android 团队已经实现了这一点。我只保留每个滚动视图的几个项目以保持快速,同时使用 LinkedList 来保证重绘时的定位。它并不理想,但功能足够好。
    【解决方案6】:

    图库是最好的解决方案,我已经尝试过了。我正在开发一个邮件应用程序,其中收件箱中的邮件显示为列表视图,我想要一个水平视图,我只是将列表视图转换为图库,并且一切正常,没有任何错误。对于滚动效果,我为画廊启用了手势监听器。我希望这个答案可以帮助你。

    【讨论】:

    • “我为画廊启用了手势监听器”是什么意思?您是否只是重写了 Gallery 的 onScroll 方法并自己进行滚动?如果是这样,你是如何告诉画廊的孩子们重新定位自己的?
    • 我写了一个扩展 SimpleOnGestureListener 的类,我在其中重写了 onfling() 方法。下面的代码显示了我画廊的手势检测和手势监听器:
    • // 手势检测gestureDetector = new GestureDetector(new MyGestureDetector()); gestureListener = new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { if (gestureDetector.onTouchEvent(event)) { return true; } 返回假; } }; myGallery.setOnTouchListener(gestureListener);
    • 我真的很忙,没有机会尝试这个,但这听起来很有希望。希望这个周末我能解决它...
    • 今天试用了您的解决方案。它并不像你说的那么简单,最终它不足以满足我的需求。我会将详细信息添加到我的原始帖子中,以供任何感兴趣的人使用。 +1 努力。
    【解决方案7】:

    您是否查看过 ViewFlipper 组件?也许它可以帮助你。

    http://developer.android.com/reference/android/widget/ViewFlipper.html

    使用此组件,您可以附加两个或多个视图子项。如果您添加一些平移动画并捕获手势检测,您可以有一个很好的水平滚动。

    【讨论】:

    • 如果我实现类似主屏幕的分页视图,我肯定会研究这一点。但由于一次只能显示一件事,它不像列表视图那样真正像列表视图,并且会表现出我试图避免的中心锁定行为。
    【解决方案8】:

    我的应用在纵向模式下使用 ListView,它只是在横向模式下切换到图库。他们都使用一个 BaseAdapter。如下所示。

           setContentView(R.layout.somelayout);
           orientation = getResources().getConfiguration().orientation;
    
           if ( orientation == Configuration.ORIENTATION_LANDSCAPE )
           {
    
                Gallery gallery = (Gallery)findViewById( R.id.somegallery );
    
                gallery.setAdapter( someAdapter );
    
                gallery.setOnItemClickListener( new OnItemClickListener() {
                @Override
                public void onItemClick( AdapterView<?> parent, View view,
                        int position, long id ) {
    
                    onClick( position );
                }
            });
           }
           else
           {
                setListAdapter( someAdapter );
    
                getListView().setOnScrollListener(this);
           }    
    

    为了处理滚动事件,我从 Gallery 继承了我自己的小部件并覆盖了 onFling()。 这是layout.xml:

        <view 
        class="package$somegallery"
        android:id="@+id/somegallery" 
        android:layout_height="fill_parent" 
        android:layout_width="fill_parent">
        </view>
    

    和代码:

        public static class somegallery extends Gallery
        {
            private Context mCtx;
    
            public somegallery(Context context, AttributeSet attrs)
            {
                super(context, attrs);
                mCtx = context;
            }
    
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                    float velocityY) {
    
                ( (CurrentActivity)mCtx ).onScroll();
    
                return super.onFling(e1, e2, velocityX, velocityY);
            }
       }
    

    【讨论】:

    • 据我所见,这仍然具有画廊的中心锁定行为。您的活动的onScroll() 方法有什么作用?
    • 实际上我在调用 onScroll() 之前在这里删除了一些代码,因为它没有涵盖问题。如果你感兴趣,我会调用 getLastVisiblePosition() ,它在 onScroll 中用于确定最后一项是否可见并执行一些逻辑。关于中心锁定 - 是的,它有这种行为。
    猜你喜欢
    • 1970-01-01
    • 2020-02-21
    • 1970-01-01
    • 2023-03-03
    • 2012-05-01
    • 2011-11-12
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    相关资源
    最近更新 更多