Как сделать горизонтальный ListView в Android?

Возможный дубликат:
Горизонтальный ListView в Android?

Как и многие вещи в Android, вы бы не подумали, что это будет такая сложная проблема, но оххх, по golly, вы ошибаетесь. И, как и многие другие вещи в Android, API даже не обеспечивает достаточно расширяемую отправную точку. Я буду проклят, если я собираюсь бросить свой собственный ListView, когда все, что я хочу, - это взять вещь и повернуть ее на свою сторону.\Декламация

Хорошо, теперь, когда я закончил дымиться, расскажите о самой проблеме. Мне нужно в основном что-то вроде Gallery, но без функции блокировки центра. Мне не нужен ListView listSelector, но он приятный. В основном, я мог бы сделать то, что хочу, с LinearLayout внутри ScrollView, но мне нужно, чтобы дочерние представления выполнялись с ListAdapter, и мне бы очень хотелось иметь ресайклинг вида. И я действительно не хочу писать код макета.

Я заглянул в исходный код для некоторых из этих классов...

Галерея. Похоже, я мог использовать Галерею, если бы я переопределял большинство методов onXyz, копировал весь их исходный код, но воздержитесь от вызова scrollIntoSlots(). Но я уверен, что если я это сделаю, я натолкнулся на какое-то поле члена, которое было бы недоступным или каким-либо другим непредвиденным последствием.

AbsSpinner: Поскольку поле mRecycler является закрытым пакетом, я сомневаюсь, что смогу расширить этот класс.

AbsListView: похоже, этот класс предназначен только для вертикальной прокрутки, поэтому никакой помощи нет.

AdapterView: мне никогда не приходилось распространять этот класс напрямую. Если вы скажете мне, что это легко сделать, и что легко свернуть мой собственный RecycleBin, я буду очень скептически настроен, но я дам ему шанс.

Предположим, что я мог бы скопировать как AbsSpinner, так и Gallery, чтобы получить то, что я хочу... надеюсь, что эти классы не используют какую-либо приватную переменную пакета, к которой я не могу получить доступ. Неужели вы думаете, что это хорошая практика? Есть ли у кого-нибудь учебники или сторонние решения, которые могут привести меня в правильном направлении?

Update:
Единственное решение, которое я нашел до сих пор, - это все сделать сам. Поскольку я задал этот вопрос, я переопределил AdapterView и реализовал собственный "HorizontalListView" с нуля. Единственный способ по-настоящему переопределить функцию блокировки центра галереи - переопределить частный scrollIntoSlots метод, который, как мне кажется, потребует создания подкласса во время выполнения. Если вы достаточно смелы, чтобы сделать это, это, возможно, лучшее решение, но я не хочу полагаться на недокументированные методы, которые могут измениться.

Swathi EP ниже предложил, чтобы я дал Gallery a OnTouchListener и переопределить функциональность прокрутки. Если вам не нужно иметь поддержку fling в вашем списке, или если все в порядке, если взгляды будут привязаны к центру в конце анимации fling, тогда это сработает для вас! Однако, в конце концов, по-прежнему невозможно исключить функцию блокировки центра, не снимая опорную опору. И я спрашиваю вас, какой список не бросается?

Итак, увы, это не сработало для меня.:-( Но если вас интересует этот подход, читайте дальше...

Мне также пришлось внести некоторые дополнения в код Swathi, чтобы получить то, что я хотел. В GestureListener.onTouch, помимо делегирования детектору жестов, мне также пришлось возвращать значение true для событий ACTION_UP и ACTION_CANCEL. Это успешно отключило функцию блокировки центра, но также отключило движение. Я смог повторно включить fling, предоставив свой собственный делегат 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 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);
        }
    }

}

Ответ 1

После прочтения этого сообщения я выполнил свой собственный горизонтальный ListView. Вы можете найти его здесь: http://dev-smart.com/horizontal-listview/ Сообщите мне, если это поможет.

Ответ 2

Изучали ли вы HorizontalScrollView, чтобы обернуть элементы списка? Это позволит каждому элементу списка быть горизонтально прокручиваемым (то, что вы вкладываете в него, зависит от вас и может сделать их динамическими элементами, похожими на ListView). Это будет хорошо работать, если вы только после одного ряда элементов.

Ответ 3

Вы знаете, возможно, можно использовать существующий ListView с некоторым разумным переопределением dispatchDraw() (для поворота холста на 90 градусов), onTouch() (чтобы поменять местами X и Y кодов MotionEvent) и, возможно, onMeasure() или что-то другое, чтобы обмануть его в мышление y на x, а не x на y...

Я понятия не имею, действительно ли это будет работать, но было бы интересно узнать.:)

Ответ 4

Это может быть очень поздний ответ, но он работает для нас. Мы используем ту же галерею, которую предоставляет Android, именно так, мы скорректировали левый край таким образом, чтобы оставшиеся экраны считались центром галереи. Это действительно сработало для нас.

Ответ 5

Я использовал Pauls (см. его ответ) Реализация HorizontalListview и его работает, большое спасибо за обмен!

Я немного изменил свой HorizontalListView-Class (кстати, у Paul есть опечатка в вашем имени класса, ваше имя класса - "HorizontialListView" вместо "HorizontalListView", "i" - это слишком много), чтобы обновлять дочерние представления при выборе.

ОБНОВЛЕНИЕ: Мой код, который я опубликовал здесь, был неправильным. Я полагаю, поскольку у меня возникли проблемы с выбором (я думаю, что это связано с переходом на просмотр), мне нужно вернуться к рисунку доска...

ОБНОВЛЕНИЕ 2: Ок Проблема решена, я просто прокомментировал "removeNonVisibleItems (dx);" в "onLayout (..)", я думаю, это повредит производительности, но поскольку я использую только очень маленькие списки, это не проблема для меня.

Я в основном использовал этот учебник здесь, в developerlife и только что заменил ListView на Pauls HorizontalListView, и внес изменения, чтобы разрешить "постоянный" выбор (ребенок, на который нажата кнопка, меняет свой внешний вид, а когда его снова нажимает, он меняет его).

Я новичок, поэтому, возможно, многие уродливые вещи в коде, дайте мне знать, если вам нужно больше деталей.

Ответ 6

Галерея - лучшее решение, я попробовал. Я работал над одним почтовым приложением, в котором письма в папке "Входящие" отображаются в виде списка, мне нужен горизонтальный вид, я просто конвертировал список в галерею, и все работало отлично, как мне было нужно без ошибок. Для эффекта прокрутки я включил прослушиватель жестов для галереи. Надеюсь, этот ответ поможет вам.

Ответ 7

Вы заглянули в компонент ViewFlipper? Может быть, это может вам помочь.

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

С помощью этого компонента вы можете прикрепить два или более дочерних элемента просмотра. Если вы добавите некоторую трансляционную анимацию и обнаружение Gesture, вы можете иметь красивую горизонтальную прокрутку.

Ответ 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);
       }    

Для обработки событий прокрутки я унаследовал свой собственный виджет из галереи и переопределял 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);
        }
   }