2010-10-06 58 views
181

可能重复:
Horizontal ListView in Android?如何在Android中制作水平ListView?

像Android的很多东西,你就不会认为这将是这样一个困难的问题,但Phoebe:哦,天哪,你是错的。而且,就像Android中的许多事情一样,API甚至没有提供合理的可扩展起点。如果我要推出自己的ListView,那么我会被吓倒,当我想要的只是把它放在一边时。 \ rant

好吧,现在我已经完成了冒烟,让我们来谈谈问题本身。我需要的基本上与Gallery完全相同,但没有中央锁定功能。我真的不需要ListView的listSelector,但它是一件很棒的事情。大多数情况下,我可以在ScrollView内部以LinearLayout的方式做我想做的事情,但我需要孩子的意见来自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委托给画廊的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); 
     } 
    } 

} 
+0

我试过这个帖子,为我工作... http://whats-online.info/science-and-tutorials/87/Android-tutorial-Horizo​​ntal-RecyclerView-with-images-and-text-example/ – 2017-01-24 09:33:50

回答

12

你看了使用HorizontalScrollView来包装你的列表项目?这将允许您的每个列表项目可以水平滚动(您放入的内容取决于您,并且可以使它们成为类似于ListView的动态项目)。如果您只在一行项目之后,这将工作得很好。

+25

我没有采用这种方法的两个原因:(1)它没有扩展'AdapterView',所以我不能与我的'ListView'和'ListAdapter'代码交换使用它,(2)没有回收意见。也许有可能将这个功能添加到滚动视图中,但在我看来,它会搅乱滚动行为,并且它几乎与简单地扩展'AdapterView'一样多。 – 2010-10-19 13:41:59

+0

你试过水平recyclerView了吗?这个库简化了使用非常小的代码来定向你的listView的代码。用演示检查这个工作的例子http://whats-online.info/science-and-tutorials/87/Android-tutorial-Horizo​​ntal-RecyclerView-with-images-and-text-example/ – 2016-12-04 00:40:08

2

画廊是最好的解决方案,我试过了。我工作的一个邮件应用,其中在如列表视图中显示的收件箱中的邮件,我想的水平视图,我只是转换列表视图,以画廊和一切运行良好,因为我需要没有任何错误。对于滚动效果,我启用了画廊的手势监听器。我希望这个答案可以帮助你。

+0

你是什么意思的“我为这个画廊启用了手势监听器?“你刚刚重写了Gallery的'onScroll'方法并自己滚动吗?如果是这样,你怎么告诉画廊的孩子重新定位自己? – 2010-11-01 13:38:02

+0

我写了一个扩展SimpleOnGestureListener的类,其中我重写了onfling()方法。下面的代码显示了我的图库的手势检测和gesturelistener: – 2010-11-02 06:28:33

+1

//手势检测 \t \t gestureDetector = new GestureDetector(new MyGestureDetector()); \t \t gestureListener =新View.OnTouchListener(){ \t \t \t公共布尔onTouch(视图V,MotionEvent事件){ \t \t \t \t如果(gestureDetector。onTouchEvent(event)){ \t \t \t \t \t return true; \t \t \t \t} \t \t \t \t返回FALSE; \t \t \t} \t \t}; \t \t myGallery.setOnTouchListener(gestureListener); – 2010-11-02 06:29:20

-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); 
     } 
    } 
+0

据我所知,这仍然会有画廊的中心锁定行为。你的活动的'onScroll()'方法是做什么的? – 2010-11-14 00:45:07

+0

其实我在调用onScroll()之前删除了一些代码,因为它没有涵盖这个问题。如果您感兴趣,我会调用onScroll中使用的getLastVisiblePosition()来确定最后一项是否可见并执行一些逻辑。关于中心锁定 - 是的,它有这种行为。 – ackio 2010-11-15 03:51:07

0

你有没有进去看了ViewFlipper组件?也许它可以帮助你。

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

有了这个组件,您可以将两个或两个以上孩子的看法。如果添加一些翻译动画并捕获手势检测,则可以有一个很好的水平滚动。

+0

如果我曾经实现过主页式的分页视图,那么我一定会研究一下。但是由于一次只能显示一件事情,它不是真正的列表视图,并且会显示出我试图避免的中心锁定行为。 – 2010-11-14 00:48:54

4

你知道,这可能可以使用现有的ListView与dispatchDraw()一些明智的首要(90度旋转画布),onTouch()(交换X和Y的MotionEvent COORDS的),也许onMeasure ()或任何将它误以为它是由x和y,而不是X的Y ...

我不知道这是否会实际工作,但它会很有趣的发现。 :)

+3

哈哈,我喜欢你的态度......有点害怕尝试......我会首先尝试Swathi EP的建议。 – 2010-11-12 18:23:43

+0

因此,我继续实施我自己的'AdapterView'子类,因为我认为它的工作量和风险较低。虽然尝试你的建议将是一个有趣的实验。 :-) – 2010-11-14 00:46:25

+0

该技术可以适用于任何View类型的额外好处。难点在于找到实际应用! :) – 2010-11-15 17:55:25

48

阅读这篇文章后,我已经实现了我自己的水平ListView。你可以在这里找到它:http://dev-smart.com/horizontal-listview/让我知道这是否有帮助。

+0

谢谢...伟大的工作! – 2011-06-28 06:39:21

+0

这就是......神奇的工作 – 2011-07-25 08:58:01

+0

一个问题:为什么在你的实现中做线程同步?所有事情都在UI线程上运行100%。 – 2011-09-08 17:45:37

3

我用Pauls(看他的answer)Horizo​​ntalListview的实现和它的工作原理,非常感谢你的分享!我只是稍微改变了他的Horizo​​ntalListView类(btw。Paul在你的类名中有一个错字,你的类名是“Horizo​​ntialListView”而不是“Horizo​​ntalListView”,“i”太多了),以便在选择时更新子视图。

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

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

我基本上使用本教程here on developerlife,并将ListView替换为Pauls Horizo​​ntalListView,并进行了更改以允许“永久性”选择(被点击的孩子改变其外观,并且当其再次单击时它将其改变回来)。

我是初学者,所以可能很多丑陋的代码中的东西,让我知道如果你需要更多的细节。

+0

这是很好的,他得到它的工作,但它没有回收视图,数据是静态的,项目的顺序不能保证,它是真的由于缺乏评论,难以调整/修复,而不知道自己在做什么。如果你确切地知道你在做什么,你首先不需要这个解决方案。人,我希望Android团队已经实现了这一点。我只保留每个scrollview的一些项目以保持速度,同时使用LinkedList来保证重定位。这并不理想,但功能足够好。 – 2014-11-12 08:22:18

4

这可能是一个非常晚的答复,但它为我们工作。我们正在使用Android提供的相同画廊,只是我们已经调整了左边距,使画面左端的画面被视为画廊的中心。这对我们非常有效。

+1

Gallery已被弃用,并在API发布后被替换为API中的Horizo​​ntalScrollView。只需将LinearLayout放在它上面,现在可以通过代码添加项目。很好的工作,虽然没有回收大量的物品。 – 2014-11-12 08:25:19