Можно ли сделать ListView
по горизонтали? Я сделал это в виде галереи, но выбранный элемент автоматически попадает в центр экрана. Я не хочу, чтобы выбранный элемент находился в том же месте, где я щелкнул. Как я могу исправить эту проблему? Моя идея заключалась в том, чтобы установить ListView
с горизонтальной прокруткой. Поделитесь своей идеей?
Горизонтальный ListView в Android?
Ответы (19)
Согласно документации Android RecyclerView
- это новый способ организации элементы в списке и отображаться по горизонтали
Преимущества:
- Поскольку при использовании адаптера Recyclerview автоматически реализуется шаблон ViewHolder
- Анимацию выполнить несложно
- Еще много возможностей
Дополнительная информация о RecyclerView
:
Образец:
Просто добавьте блок ниже, чтобы сделать ListView
горизонтальным из вертикального
Фрагмент кода
LinearLayoutManager layoutManager= new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL, false);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(layoutManager);
Пол не пытается исправить ошибки в своей библиотеке или принять исправления пользователей. Вот почему я предлагаю другую библиотеку с аналогичной функциональностью:
https://github.com/sephiroth74/HorizontalVariableListView
Обновление: 24 июля 2013 г. автор (sephiroth74) выпустил полностью переписанную версию, основанную на коде Android 4.2.2 ListView. Я должен сказать, что в нем нет всех ошибок, которые были в предыдущей версии, и он отлично работает!
Ответ @Paul ссылается на отличное решение, но код не позволяет использовать onClickListeners для дочерних элементов (функции обратного вызова никогда не вызываются). Некоторое время я пытался найти решение, и я решил опубликовать здесь то, что вам нужно изменить в этом коде (на случай, если это кому-то понадобится).
Вместо переопределения dispatchTouchEvent
переопределить onTouchEvent
. Используйте тот же код dispatchTouchEvent
и удалите метод (разницу между ними можно прочитать здесь http://developer.android.com/guide/topics/ui/ui-events.html#EventHandlers)
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = mGesture.onTouchEvent(event);
return handled;
}
Затем добавьте следующий код, который решит украсть событие у дочерних элементов и передать его нашему onTouchEvent
или позволить им обработать его.
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch( ev.getActionMasked() ){
case MotionEvent.ACTION_DOWN:
mInitialX = ev.getX();
mInitialY = ev.getY();
return false;
case MotionEvent.ACTION_MOVE:
float deltaX = Math.abs(ev.getX() - mInitialX);
float deltaY = Math.abs(ev.getY() - mInitialY);
return ( deltaX > 5 || deltaY > 5 );
default:
return super.onInterceptTouchEvent(ev);
}
}
Наконец, не забудьте объявить переменные в своем классе:
private float mInitialX;
private float mInitialY;
dispatchTouchEvent
.
- person ticofab; 15.08.2013
Поскольку Google представила Android Support Library v7 21.0.0, вы можете использовать RecyclerView для горизонтальной прокрутки элементов. Виджет RecyclerView - это более продвинутая и гибкая версия ListView.
Чтобы использовать RecyclerView, просто добавьте зависимость:
com.android.support:recyclerview-v7:23.0.1
Вот образец:
public class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
MyAdapter adapter = new MyAdapter(myDataset);
recyclerView.setAdapter(adapter);
}
}
Дополнительная информация о RecyclerView:
- https://developer.android.com/training/material/lists-cards.html
- https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
Это немного (очень) поздно, но я отправляю это на случай, если кто-то придет позже.
В библиотеке поддержки предварительной версии Android L есть RecyclerView
, который делает именно то, что вы хотите.
Прямо сейчас вы можете получить его только через L preview SDK, и вам нужно установить minSdk
на L
. Но вы можете скопировать все необходимые файлы в свой проект и использовать их таким образом, пока L официально не выйдет.
Документы для предварительного просмотра можно загрузить здесь.
Предупреждение: API для Recycler View может измениться, и в нем могут быть ошибки.
Обновлено
Исходный код для горизонтального просмотра списка:
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
RecyclerView myList = findViewById(R.id.my_recycler_view);
myList.setLayoutManager(layoutManager);
LinearLayoutManager
- см. Здесь: stackoverflow.com/questions/28460300/
- person Stéphane; 08.04.2015
Загрузите файл jar с здесь
теперь поместите его в папку libs, щелкните его правой кнопкой мыши и выберите «Добавить как библиотеку».
теперь в main.xml поместите этот код
<com.devsmart.android.ui.HorizontalListView
android:id="@+id/hlistview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
теперь в классе Activity, если вы хотите горизонтальный список с изображениями, поместите этот код
HorizontalListView hListView = (HorizontalListView) findViewById(R.id.hlistview);
hListView.setAdapter(new HAdapter(this));
private class HAdapter extends BaseAdapter {
LayoutInflater inflater;
public HAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return Const.template.length;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
HViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.listinflate, null);
holder = new HViewHolder();
convertView.setTag(holder);
} else {
holder = (HViewHolder) convertView.getTag();
}
holder.img = (ImageView) convertView.findViewById(R.id.image);
holder.img.setImageResource(Const.template[position]);
return convertView;
}
}
class HViewHolder {
ImageView img;
}
На самом деле это очень просто: просто поверните список, чтобы положить его на бок
mlistView.setRotation(-90);
Затем, после надувания дочерних элементов, это должно быть внутри метода getView. вы поворачиваете детей, чтобы они встали прямо:
mylistViewchild.setRotation(90);
Изменить: если ваш ListView не помещается должным образом после поворота, поместите ListView внутри этого RotateLayout вот так:
<com.github.rongi.rotate_layout.layout.RotateLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:angle="90"> <!-- Specify rotate angle here -->
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</com.github.rongi.rotate_layout.layout.RotateLayout>
Мое решение - просто использовать виджет ViewPager
. Он не заблокирован по центру, как Gallery
, и имеет встроенные функции для повторного использования представлений (как ListView
). Вы можете увидеть аналогичный подход в приложении Google Play, когда имеете дело с горизонтально прокручиваемыми списками.
Вам просто нужно расширить PagerAdapter
и выполнить там пару настроек:
public class MyPagerAdapter extends PagerAdapter {
private Context mContext;
public MyPagerAdapter(Context context) {
this.mContext = context;
}
// As per docs, you may use views as key objects directly
// if they aren't too complex
@Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.item, null);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
return 10;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
// Important: page takes all available width by default,
// so let's override this method to fit 5 pages within single screen
@Override
public float getPageWidth(int position) {
return 0.2f;
}
}
В результате у вас будет виджет с горизонтальной прокруткой с адаптером, например:
Примечание. Android теперь поддерживает горизонтальные представления списков с помощью RecyclerView, поэтому теперь этот ответ устарел для информации о RecyclerView: https://developer.android.com/reference/android/support/v7/widget/RecyclerView
Я разработал логику, чтобы сделать это без использования какой-либо внешней библиотеки горизонтальной прокрутки, вот полученный мной горизонтальный вид, и я разместил свой ответ здесь: https://stackoverflow.com/a/33301582/5479863
Мой ответ json таков:
{"searchInfo":{"status":"1","message":"Success","clist":[{"id":"1de57434-795e-49ac-0ca3-5614dacecbd4","name":"Theater","image_url":"http://52.25.198.71/miisecretory/category_images/movie.png"},{"id":"62fe1c92-2192-2ebb-7e92-5614dacad69b","name":"CNG","image_url":"http://52.25.198.71/miisecretory/category_images/cng.png"},{"id":"8060094c-df4f-5290-7983-5614dad31677","name":"Wine-shop","image_url":"http://52.25.198.71/miisecretory/category_images/beer.png"},{"id":"888a90c4-a6b0-c2e2-6b3c-561788e973f6","name":"Chemist","image_url":"http://52.25.198.71/miisecretory/category_images/chemist.png"},{"id":"a39b4ec1-943f-b800-a671-561789a57871","name":"Food","image_url":"http://52.25.198.71/miisecretory/category_images/food.png"},{"id":"c644cc53-2fce-8cbe-0715-5614da9c765f","name":"College","image_url":"http://52.25.198.71/miisecretory/category_images/college.png"},{"id":"c71e8757-072b-1bf4-5b25-5614d980ef15","name":"Hospital","image_url":"http://52.25.198.71/miisecretory/category_images/hospital.png"},{"id":"db835491-d1d2-5467-a1a1-5614d9963c94","name":"Petrol-Pumps","image_url":"http://52.25.198.71/miisecretory/category_images/petrol.png"},{"id":"f13100ca-4052-c0f4-863a-5614d9631afb","name":"ATM","image_url":"http://52.25.198.71/miisecretory/category_images/atm.png"}]}}
Файл макета:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="5">
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4" />
<HorizontalScrollView
android:id="@+id/horizontalScroll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
файл класса:
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.ll);
for (int v = 0; v < collectionInfo.size(); v++) {
/*---------------Creating frame layout----------------------*/
FrameLayout frameLayout = new FrameLayout(ActivityMap.this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, getPixelsToDP(90));
layoutParams.rightMargin = getPixelsToDP(10);
frameLayout.setLayoutParams(layoutParams);
/*--------------end of frame layout----------------------------*/
/*---------------Creating image view----------------------*/
final ImageView imgView = new ImageView(ActivityMap.this); //create imageview dynamically
LinearLayout.LayoutParams lpImage = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
imgView.setImageBitmap(collectionInfo.get(v).getCatImage());
imgView.setLayoutParams(lpImage);
// setting ID to retrieve at later time (same as its position)
imgView.setId(v);
imgView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// getting id which is same as its position
Log.i(TAG, "Clicked on " + collectionInfo.get(v.getId()).getCatName());
// getting selected category's data list
new GetSelectedCategoryData().execute(collectionInfo.get(v.getId()).getCatID());
}
});
/*--------------end of image view----------------------------*/
/*---------------Creating Text view----------------------*/
TextView textView = new TextView(ActivityMap.this);//create textview dynamically
textView.setText(collectionInfo.get(v).getCatName());
FrameLayout.LayoutParams lpText = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER);
// Note: LinearLayout.LayoutParams 's gravity was not working so I putted Framelayout as 3 paramater is gravity itself
textView.setTextColor(Color.parseColor("#43A047"));
textView.setLayoutParams(lpText);
/*--------------end of Text view----------------------------*/
//Adding views at appropriate places
frameLayout.addView(imgView);
frameLayout.addView(textView);
linearLayout.addView(frameLayout);
}
private int getPixelsToDP(int dp) {
float scale = getResources().getDisplayMetrics().density;
int pixels = (int) (dp * scale + 0.5f);
return pixels;
}
Работающий здесь трюк - это идентификатор, который я назначил ImageView «imgView.setId (v)», и после этого, применяя к нему onClickListener, я снова получаю идентификатор представления .... Я также прокомментировал внутри кода, поэтому что это легко понять, надеюсь, это может быть очень полезно ... Удачного кодирования ... :)
getPixelsToDP()
?
- person Skizo-ozᴉʞS; 05.11.2015
Это небольшой ответ, но как насчет использования просмотра с горизонтальной прокруткой а>?
Вы можете использовать RecyclerView в библиотеке поддержки. RecyclerView - это обобщенная версия ListView, которая поддерживает:
- Диспетчер компоновки для размещения элементов
- Анимация по умолчанию для обычных операций с элементами
Просмотр документов Android Recycler
Я много искал решение этой проблемы. Короткий ответ: нет хорошего решения без переопределения частных методов и тому подобных вещей. Лучшее, что я нашел, - это реализовать его самому с нуля, расширив AdapterView
. Это довольно жалко. См. Мой вопрос SO о горизонтальных ListViews.
fillListDown()
вам нужно отслеживать нижний край последнего элемента списка. Но вы не знаете, где находится этот край, пока не создадите последний элемент (adapter.getCount () - 1). Итак, вы ждете, пока этот элемент будет создан, а затем можете сохранить местоположение края. Как только вы это узнаете, вы можете убедиться, что значение mListTop никогда не меньше 0 и никогда не превышает высоту списка.
- person Neil Traft; 03.03.2011
Мне пришлось сделать то же самое для одного из моих проектов, и в итоге я написал свой собственный. Я назвал его HorzListView, теперь он является частью моей библиотеки Aniqroid с открытым исходным кодом.
http://aniqroid.sileria.com/doc/api/ (загрузите файлы на внизу или используйте проект кода Google, чтобы увидеть больше вариантов загрузки: http://code.google.com/p/aniqroid/downloads/list)
Документация по классу находится здесь: http://aniqroid.sileria.com/doc/api/com/sileria/android/view/HorzListView.html.
Для своего приложения я использую HorizontalScrollView, содержащий внутри LinearLayout, с горизонтальной ориентацией. Чтобы добавить изображения внутрь, я создаю ImageView внутри действия и добавляю их в свой LinearLayout. Например:
<HorizontalScrollView
android:id="@+id/photo_scroll"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="horizontal"
android:visibility="gone">
<LinearLayout
android:id="@+id/imageview_holder"
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_height="match_parent">
</LinearLayout>
</HorizontalScrollView>
Для меня это отлично работает. В этой деятельности все, что мне нужно сделать, это что-то вроде кода ниже:
LinearLayout imgViewHolder = findViewById(R.id.imageview_holder);
ImageView img1 = new ImageView(getApplicationContext());
//set bitmap
//set img1 layout params
imgViewHolder.add(img1);
ImageView img2 = new ImageView(getApplicationContext());
//set bitmap
//set img2 layout params
imgViewHolder.add(img2);
Как я уже сказал, это работает для меня, и я надеюсь, что это поможет кому-то, кто хочет этого достичь.
ну, вы всегда можете создавать свои текстовые просмотры и т. д. динамически и устанавливать свои onclicklisteners, как если бы вы делали с адаптером
HorizontialListView не может работать, когда данные в адаптере задействованы в другом потоке. Все работает на 100% в потоке пользовательского интерфейса. Это большая проблема в многопоточности. Я думаю, что использование HorizontialListView - не лучшее решение вашей проблемы. HorzListView - лучший способ. Просто замените предыдущую галерею на HorzListView. Вам не нужно изменять код адаптера. Тогда все пойдет так, как вы надеетесь. См. https://stackoverflow.com/a/12339708/1525777 о HorzListView.
Я использовал горизонтальный просмотр списка ссылку в моем проекте, и я получил хорошие результаты. Сначала я использовал библиотеку devsmart, но это дало мне некоторые проблемы. Так что лучший способ использовать горизонтальная ссылка на список, так как она устранила мои проблемы, а также недавно я запустил свое приложение в Google PlayStore, используя эту библиотеку, и получил хороший отклик от пользователей. Поэтому я рекомендую вам использовать ту же библиотеку, о которой я упоминал выше, для отображения списка по горизонтали. Наслаждаться :)
Для этого есть отличная библиотека под названием TwoWayView, ее очень легко реализовать, просто включите библиотеку проекта в свое рабочее пространство и добавьте ее как проект библиотеки в исходный проект, а затем выполните следующие шаги, которые изначально были упомянутый здесь:
Во-первых, давайте добавим стиль, указывающий ориентацию ListView (горизонтальную или вертикальную) в (res / values / styles.xml):
<style name="TwoWayView">
<item name="android:orientation">horizontal</item>
</style>
Потом,
В XML макета используйте следующий код, чтобы добавить TwoWayView:
<org.lucasr.twowayview.TwoWayView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/lvItems"
style="@style/TwoWayView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false"
tools:context=".MainActivity" />
и, наконец, просто объявите это и работайте с ним, как с обычным ListView
:
TwoWayView lvTest = (TwoWayView) findViewById(R.id.lvItems);
Все методы ListView
будут работать здесь как обычно, но я заметил только одно отличие: при установке режима выбора метод setChoiceMode
принимает не значение int
, а значение из enum
, называемое ChoiceMode
, поэтому list_view.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
будет lvTest.setChoiceMode(ChoiceMode.SINGLE); // or MULTIPLE or NONE
.
Вы можете использовать ViewFlipper для включения XML макета и добавления изображений, просмотра списка для каждого XML макета