如何在Android中制作水平ListView?

时间:2010-10-06 21:28:24

标签: android listview gallery

  

可能重复:
  Horizontal ListView in Android?

像Android中的很多东西一样,你不会认为这会是一个如此难的问题,但哦,哦,你会错的。而且,与Android中的许多内容一样,API甚至没有提供合理可扩展的起点。如果我打算推出自己的ListView,我会被诅咒,当我想要的只是把它拿走并转向它的时候。 \咆哮

好的,既然我已经完成了冒烟,那就让我们来谈谈问题本身。我需要的基本上与Gallery完全相同,但没有中心锁定功能。我真的不需要ListView的listSelector,但它是一个不错的选择。大多数情况下,我可以在LinearLayout内使用ScrollView执行我想要的操作,但我需要来自ListAdapter的子视图,我真的希望有一个视图回收器。我真的不想写任何布局代码。

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

图库:如果我覆盖了大多数'onXyz'方法,我可以使用图库,复制所有源代码,但不要调用scrollIntoSlots()。但我敢肯定,如果我这样做,我会遇到一些无法访问的成员字段或其他一些无法预料的后果。

AbsSpinner:由于mRecycler字段是包私有的,我怀疑我是否可以扩展此类。

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

AdapterView:我从来没有直接扩展这个类。如果你告诉我它很容易做,并且很容易自己推出RecycleBin,我会非常怀疑,但我会试一试。

我想我可以复制 AbsSpinnerGallery以获得我想要的东西......希望这些类不使用某些包私有变量我可以不能进入。你们都认为这是一个好习惯吗?有没有人有任何教程或第三方解决方案可能会让我朝着正确的方向前进?

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

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

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

我还必须对Swathi的代码添加一些内容以获得我想要的内容。在GestureListener.onTouch中,除了委托给手势检测器之外,我还必须为ACTION_UPACTION_CANCEL事件返回true。这成功禁用了中心锁定功能,但它也禁用了投掷功能。通过将我自己的GestureListener委托给Gallery的onFling方法,我能够重新启用fling。如果您想尝试一下,请进入您的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);
        }
    }

}

8 个答案:

答案 0 :(得分:48)

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

答案 1 :(得分:12)

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

答案 2 :(得分:4)

你知道,可能可能可以使用现有的ListView,同时明智地覆盖dispatchDraw()(将画布旋转90度),onTouch()(交换MotionEvent coords的X和Y)也许onMeasure()或其他什么来欺骗它认为它是x而不是x by y ...

我不知道这是否真的有效,但找出它会很有趣。 :)

答案 3 :(得分:4)

这可能是一个非常晚的回复,但它对我们有用。我们正在使用Android提供的相同图库,我们已经调整了左边距,使得屏幕左端被视为图库的中心。这真的对我们很有用。

答案 4 :(得分:3)

我使用Pauls(请参阅他的answer)Horizo​​ntalListview的实现,它有效,非常感谢您的分享!

我略微改变了他的Horizo​​ntalListView-Class(顺便说一句。保罗你的类名中有一个拼写错误,你的类名是“Horizo​​ntialListView”而不是“Horizo​​ntalListView”,“i”太多了)在选择时更新子视图。

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

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

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

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

答案 5 :(得分:2)

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

答案 6 :(得分:0)

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

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

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

答案 7 :(得分:-1)

我的应用程序在portraint模式下使用ListView,只需在横向模式下切换到Gallery。它们都使用一个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);
        }
   }