ImageView in ViewPager doesnt get correct Height - android

I have a list with userEntries. Each of these entries contains one block where I have some dynamic content, in this case a viewpager containing 1-3 images. My problem comes when I want to these images to have the correct Height. If I set my ViewPager height to for example 100dp the image is shown and the ViewPager works as expected. However if I rotate the phone or use a screen that is abit larger the image stays the same width as the 100dp is limiting it from getting the correct width. As this happends I want to use wrap_content so it resizes its height in order to fill the image to its full width, but when I do that the layout is minimized. See screens and code below.
CustomViewHolder.java <- This holds the userentry
public void populateContentArea(int mListType, Context context){
switch (mListType){
case Constants.ListType.DATE_LIST:
DatingEntryModel dateUser = (DatingEntryModel)mUser;
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View datingContent = layoutInflater.inflate(R.layout.dating_picture_container, llUserEntryContent, true);
ViewPager datingPager = (ViewPager) datingContent.findViewById(R.id.datingPager);
datingPager.setAdapter(new DatingPictureAdapter(context, dateUser.activities));
TabLayout datingIndicator = (TabLayout) datingContent.findViewById(R.id.datingTabDots);
datingIndicator.setupWithViewPager(datingPager);
break;
default:
break;
}
}
dating_picture_container.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v4.view.ViewPager
android:id="#+id/datingPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.design.widget.TabLayout
android:id="#+id/datingTabDots"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_width="70dp"
android:layout_height="20dp"
app:tabIndicatorHeight="0dp"
app:tabPaddingBottom="10dp"
app:tabBackground="#drawable/tab_dot_selector"
app:tabGravity="center"/>
</RelativeLayout>
DatingPictureAdapter.java
public class DatingPictureAdapter extends PagerAdapter {
private Context mContext;
List<DatingActivity> mActivities;
public DatingPictureAdapter(Context context, List<DatingActivity> activities) {
mContext = context;
mActivities = activities;
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
DatingActivity activity = mActivities.get(position);
final ImageView datingBanner = new ImageView(mContext);
datingBanner.setAdjustViewBounds(true);
datingBanner.setScaleType(ImageView.ScaleType.FIT_CENTER);
UrlManager.loadDatingBannerGlide(mContext, activity.id).into(datingBanner);
collection.addView(datingBanner, 0);
return datingBanner;
}
#Override
public int getCount() {
return mActivities.size();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public void destroyItem(ViewGroup collection, int position, Object view) {
collection.removeView((View) view);
}
}
At the end of the day, setting the height static to 10dp and then resize depending on the image width was the best solution for me. Thanks for all responses :)

I think it's because of you set your ViewPager's height "wrap_content" wrap_content does not work for ViewPager. If you change it to match_parent or to a static dp your issue'll be fixed.
<android.support.v4.view.ViewPager
android:id="#+id/datingPager"
android:layout_width="match_parent"
android:layout_height="144dp"/>

I think you have 2 ways to do that:
1- if you want to use differents layouts with different orientation you must create one layout for each orientation
2- you can get progammatically get the height of display and put inside layout dinamically proportional height
i hope this help you

Related

Android how to use recyclerview to scroll horizontally in large vertical data set

I want to use recylerview to show a large vertical data set with a large amount of data on each row
I am currently using the LinearLayoutManager
Each row has it's own ViewHolder where I hold a "RowView" object. Each RowView is a fixed width.
public class EventsRecylerAdapter extends RecyclerView.Adapter<EventsRecylerAdapter.ViewHolder>
{
private Context mContext;
private ArrayList<EventsStrip.Item> mItems;
public EventsRecylerAdapter(Context context, ArrayList<EventsStrip.Item> items) {
mContext = context;
mItems = items;
}
#Override
public EventsRecylerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
EventRowView v = new EventRowView(mContext);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
EventsStrip.Item item = mItems.get(position);
holder.mEventRowView.initItem(item);
}
#Override
public int getItemCount() {
return mItems.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public EventRowView mEventRowView;
public int getMeasuredHeight() {
mEventRowView.measure(0, 0);
return mEventRowView.getMeasuredHeight();
}
public ViewHolder(EventRowView v) {
super(v);
mEventRowView = v;
}
}
}
Here is the code in my "EventsView" container that has the
protected void init(Context context) {
setOrientation(LinearLayout.VERTICAL);
ViewGroup.LayoutParams params = (ViewGroup.LayoutParams) getLayoutParams();
if (null == params)
params = generateDefaultLayoutParams();
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
setLayoutParams(params);
mHeaderScrollView = (HorizontalScrollView) findViewById(R.id.headerScrollView);
mHeaderScrollView.getViewTreeObserver().addOnScrollChangedListener(new HeaderScrollViewListener());
mRecyclerView = (RecyclerView) findViewById(R.id.EventsRecylerView);
mRecyclerView.addOnScrollListener(new RecyclerScrollListener());
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(mActivity);
mRecyclerView.setLayoutManager(mLayoutManager);
}
Since each row each wider than the device I would like to enable a horizontal scrollbar so I can scroll sideways to see more data in each row. Currently I am seeing all the data that I would expect to see just clipped at the edge of the screen.
Also, I would like to scroll horizontally programatically. When I call scrollTo(newxvalue, recylerview.getScrollY()) nothing happens. I don't want to scroll to a new position in the adapter list. I want to see more data that is currently "off screen" on the items currently on the screen. I have a static header at the top of the "list" with a horizontal scroll view on it. As the user drags the header I want the list below to stay in sync.
Any help/guidance would be appreciated
This turned out to be a lot simpler than I thought.
In a list view you will get unpredictable results if you put a list view inside a ScrollView . However in the case I simply put the header view and the RecylerView with a vertical linear layout manager inside of a horizontal ScrollView and it seems to behave as intended (i.e. RecylerView does not have the same limitations as ListView).
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/headerScrollView"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:background="#color/navigation_bar_color"
android:id="#+id/eventRowLayout"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingLeft="2dp"
android:layout_alignParentTop="true" />
<android.support.v7.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/EventsRecylerView"
android:layout_below="#+id/eventRowLayout">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
</HorizontalScrollView>

How to get a layout size before it's displayed?

I inflate a layout containing an ImageView and a RelativeLayout. The layout has its width set to match_parent and its height set to wrap_content. The height is determined by the ImageView, but the image set to the ImageView is loaded dynamically from the internet.
Since I know the image ratio, I'd like to set the size of the ImageView before it's displayed to avoid a jump in the UI due to the change in the layout height when the image is set.
To set the size I need the layout width, to compute the ImageView height.
I tried
int width = header.getMeasuredWidth();
but since the layout is not drawn yet it returns 0
I also tried to use measure before, as suggested here
header.measure(0, 0);
int width = header.getMeasuredWidth();
but measure returns a NullPointerException
How can I do that?
list_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/pic"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:id="#+id/header_text_container"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:background="#789987">
</RelativeLayout>
</RelativeLayout>
MyListFragment.java
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mListView = (ListView) getView().findViewById(R.id.listview);
View header = getActivity().getLayoutInflater().inflate(R.layout.list_header, null);
int width = header.getMeasuredWidth(); // 0 because not drawn yet
ImageView pic = (ImageView) header.findViewById(R.id.pic);
pic.setLayoutParams(new LayoutParams(width, (int) (width/imgRatio)));
pic.invalidate();
header.invalidate();
/* ... */
}
You could try with a ViewTreeObserver.
Your Fragment/Activity should implement OnGlobalLayoutListener, in OnActivityCreate declare handler
ViewGroup container = (ViewGroup) findViewById(R.id.header_text_container)
ViewTreeObserver vto = container.getViewTreeObserver();
vto.addOnGlobalLayoutListener(this);
and put your width logic in listener when size are available
#Override
public void onGlobalLayout() {
// Remember to remove handler
container.getViewTreeObserver().removeGlobalOnLayoutListener(this);
View header = getActivity().getLayoutInflater().inflate(R.layout.list_header, null);
int width = header.getMeasuredWidth(); // 0 because not drawn yet
...
}
you can get the size in Activity's onWindowFocusChanged(boolean hasFocus) method.
You can get the width and height of a view in onGlobalLayout, wich is a method to Callback method wich is invoked when the global layout state or the visibility of views within the view tree changes (thus as soon as the view is drawn and the size is known).
ViewTreeObserver vto = header.getViewTreeObserver();
if(vto!=null){
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
header.getWidth();
header.getHeight();
}
});
}

Align the child views in center of the ViewPager android

I need to set the child view as center of the ViewPager and also I would like to show some part of the next and previous views to the current view sides(like current screen below 1). But currently the current view is starting at left side of the ViewPager(like expected screen below 2). How can I achieve that?
Here is my code..
MyViewPagerAdapter
public class MyViewPagerAdapter extends PagerAdapter {
private Activity mActivity;
private int mPageCount;
public MyViewPagerAdapter(Activity activity,int pageCount) {
mActivity = activity;
mPageCount = pageCount;
}
#Override
public int getCount() {
return mPageCount;
}
#Override
public boolean isViewFromObject(View view, Object obj) {
return (view ==(View)obj);
}
#Override
public Object instantiateItem(ViewGroup container,final int position) {
ViewGroup viewGroup = (ViewGroup)mActivity.getLayoutInflater().inflate(
R.layout.item_view, null);
viewGroup.setBackgroundColor(randomColor());
TextView textView = (TextView)viewGroup.findViewById(R.id.textView1);
textView.setText("Page: "+(position+1));
Button button = (Button) viewGroup.findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mActivity, "Hey, Its clicked!!! at page "+(position+1), Toast.LENGTH_LONG).show();
}
});
container.addView(viewGroup);
return viewGroup;
}
Random rnd = new Random();
private int randomColor(){
return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
}
#Override
public void destroyItem(ViewGroup collection, int position, Object view) {
//must be overridden else throws exception as not overridden.
Log.d("Tag", collection.getChildCount()+"");
collection.removeView((View) view);
}
#Override
public float getPageWidth(int position) {
return 0.8f;
}
}
MainActivity
public class MainActivity extends Activity {
private ViewPager viewPager;
LinearLayout linearLayout;
private int ID = 100;
private final int count = 8;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.viewPager);
linearLayout = (LinearLayout) findViewById(R.id.indicator_layout);
generateIndicators(count);
MyViewPagerAdapter adapter = new MyViewPagerAdapter(this, count);
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
int oldPosition = 0;
#Override
public void onPageSelected(int position) {
//this changes the old position's view state image
((TextView)linearLayout.getChildAt(oldPosition)).setText("");
oldPosition = position;
//this changes the current position's view state image
((TextView)linearLayout.getChildAt(position)).setText((position+1)+"");
}
//this method will be called repeatedly upto another item comes as front one(active one)
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
//this will be called as per scroll state
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
viewPager.setOffscreenPageLimit(4);
}
private void generateIndicators(int count) {
/// Converts 14 dip into its equivalent px
int padd = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());
for(int i=0;i<count;i++){
TextView textView = new TextView(this);
textView.setId(ID+i);
final int currentItem = i;
textView.setBackgroundResource(R.drawable.white_cell);
textView.setPadding(padd,padd,padd,padd);
/// Converts 14 dip into its equivalent px
int size = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics());
textView.setTextSize(size);
textView.setGravity(Gravity.CENTER);
/// Converts 14 dip into its equivalent px
int px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(px, px);
linearLayout.addView(textView,params);
}
((TextView)linearLayout.getChildAt(0)).setText("1");
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</android.support.v4.view.ViewPager>
<LinearLayout
android:id="#+id/indicator_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="19dp" >
</LinearLayout>
</RelativeLayout>
item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="match_parent"
android:id="#+id/root_view"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="click me" />
</LinearLayout>
Current screen
expected screen
For one app I implemented similar the following way, with standard ViewPager:
Make pages full-screen with the actual content in an inner layout. For example, make the full-screen layout a RelativeLayout with transparent background and the actual content another RelativeLayout centered in the parent. If I remember right, the reason for this was that with just the inner layout as a page, the ViewPager would not have taken all the screen width on some devices such as Galaxy Nexus.
Use ViewPager.setPageMargin() to set up a negative page margin i.e. how much of the next/previous page you want to show. Make sure it only overlaps the transparent region of the parent full-screen layout.
Call ViewPager.setOffscreenPageLimit() to adjust the off-screen page count to at least 2 from the default 1 to ensure smooth paging by really creating the pages off-screen. Otherwise you will see next/previous pages being drawn while already partially showing on screen.
For anyone upset that the OP didn't update his question with the solution here is a link that explains, with minimal effort, how to pull this off in XML: http://blog.neteril.org/blog/2013/10/14/android-tip-viewpager-with-protruding-children/
Basically when you declare your viewpager in XML, give it the same left and right padding and set android:clipToPadding="false". (The clipToPadding is missing in his xml sample and necessary to achieve this effect)
Finally, I have added my solution for this question in GitHub. I have done some pretty tricks to get the workaround solution. You can get the project from the below link(Actually I have planned to create a blog with the explanation , but I dint have that much time to do).
Here is the link(https://github.com/noundla/Sunny_Projects/tree/master/CenterLockViewPager)
You have to copy the files from com.noundla.centerviewpagersample.comps package to your project. And you can see the usage of that Viewpager in MainActivity class.
Please let me know if anyone has problems with this.
I found solution in this post, below the code i used:
// Offset between sibling pages in dp
int pageOffset = 20;
// Visible part of sibling pages at the edges in dp
int sidePageVisibleWidth = 10;
// Horizontal padding will be
int horPadding = pageOffset + sidePageVisibleWidth;
// Apply parameters
viewPager.setClipToPadding(false);
viewPager.setPageMargin(UIUtil.dpToPx(pageOffset, getContext()));
viewPager.setPadding(UIUtil.dpToPx(horPadding, getContext()), 0, UIUtil.dpToPx(horPadding, getContext()), 0);
dpToPx code:
public static int dpToPx(int dp, Context context) {
float density = context.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
This is all you need
You can use padding for viewPager and set clipToPadding false
Java
viewPager.setClipToPadding(false);
viewPager.setPadding(50, 0, 50, 0);
Kotlin
viewPager.clipToPadding = false
viewPager.setPadding(50, 0, 50, 0)
I had to center current page in view pager with different page widths, so solution with paddings was not suitable. Also user scrolling was disabled (it was tab bar view pager, scrolled by another view pager). Here is a very simple solution to do that - just override ViewPager.ScrollTo method just like this (C# code, Xamarin):
public override void ScrollTo(int x, int y)
{
x -= (int) (MeasuredWidth * (1 - Adapter.GetPageWidth(CurrentItem)) / 2);
base.ScrollTo(x, y);
}
And if you calculate page width for each fragment don't forget to cache them in array.
Extend HorizontalScrollView class as the parent for the scrolling view. In the onMeasure() method you can specify the width and height of each child. Little cumbersome way but the effect will be good and you can have a good hold on your child view.

Gallery layout problems

I'm trying to create a gallery in my Android project, but can't seem to make it work as it should.
Here is what I am trying to do:
Basically, I want a gallery that displays only one picture, but still has the same controls (sliding left and right). I'm getting the pictures asynchronously from the internet.
Where am I at that?
Well, for now I'm just trying to display the gallery... I'll see the "one picture" step after.
What is my problem?
Well... My gallery doesn't show on my layout. At all.
What seems to be the problem?
Log information give me that the height and the widht of my gallery object are... 0.
Speaking about code...
Layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#drawable/fond">
<ImageView android:background="#drawable/banniere" android:layout_height="wrap_content" android:layout_width="fill_parent"/>
<Gallery
android:id="#+id/gallery"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.galleryfullscreen);
Gallery gal = (Gallery) findViewById(R.id.gallery);
ArrayList<Photo> photos = (ArrayList<Photo>) this.getIntent().getExtras().getSerializable("Photos");
int pos = this.getIntent().getExtras().getInt("Index");
Log.i("Season",photos.toString());
gal.setAdapter(new PhotoAdapter(this, photos, false));
gal.setSelection(pos);
Log.d("Bottom", String.valueOf(gal.getBottom()));
Log.d("Right", String.valueOf(gal.getRight()));
Log.d("Width", String.valueOf(gal.getWidth()));
Log.d("Height", String.valueOf(gal.getHeight()));
}
Extras are correctly set.
Adapter
public class PhotoAdapter extends BaseAdapter {
private ArrayList<PhotoView> views;
private ArrayList<Photo> season;
public PhotoAdapter(Context c, ArrayList<Photo> images, boolean mini) {
season = images;
views = new ArrayList<PhotoView>();
for (int i = 0; i < images.size() ; i++)
if (mini)
views.add(new PhotoView(c,images.get(i).getUrlMini(),false));
else{
views.add(new PhotoView(c,images.get(i).getUrlBig(),true));
}
}
#Override
public int getCount() {
return views.size();
}
#Override
public Object getItem(int position) {
return views.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return views.get(position);
}
public ArrayList<Photo> getSeason() {
return season;
}
}
This adaptor is working fine for other objects (such as gridview)
And the PhotoView...
public class PhotoView extends ImageView {
Bitmap image;
public PhotoView(Context context, String url, boolean Gallery) {
super(context);
if (!Gallery){
this.setLayoutParams(new GridView.LayoutParams(GridView.LayoutParams.WRAP_CONTENT,GridView.LayoutParams.WRAP_CONTENT));
this.setScaleType(ImageView.ScaleType.FIT_CENTER);
this.setImageDrawable((getResources().getDrawable(R.drawable.blk)));
new DownloadImageTask().execute(url);
}
else{
this.setLayoutParams(new Gallery.LayoutParams(100,100));
this.setScaleType(ImageView.ScaleType.FIT_CENTER);
this.setBackgroundDrawable(((getResources().getDrawable(R.drawable.blk))));
this.setImageDrawable((getResources().getDrawable(R.drawable.blk)));
}
}
public Bitmap getImage(){
return image;
}
public void setImage(Bitmap img){
image = img;
this.setImageBitmap(image);
}
The same, it's working fine for a grid view. With the download task, that is not set up for the Gallery yet. I'm at least trying to display a ressource before setting this up, so I'm sure the problem doesn't come from here.
Sooo... why are my logs showing "0" for gallery width and height? LayoutParams are set for the object (I tried both XML and code), and for the views inside. The items are created, and the "getView" method is called. I tried seeting up LayoutParams here, doesn't change anything.
I also tried adding the xmlns:android="http://schemas.android.com/apk/res/android" part in the layout for the gallery, doesn't change a thing.
I really don't know what I'm missing. I tried to adapt many tutorials on gallery objects, but still... don't know!
If you have any idea of what I should do/try, please...
Thanks all!
Cheers
Looking at your XML file I can see what could be problem. Your LinearLayout is set to add views in an horizontal matter (that is default) and your first view has its layout_width set to fill_parent.
Set orientation to vertical in your LinearLayout.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#drawable/fond">

Horizontal ListView in Android?

Is it possible to make the ListView horizontally? I have done this using a gallery view, but the selected item comes to the center of the screen automatically. I don't want the selected item at the same spot I clicked. How can I rectify this problem? My idea was to set the ListView with a horizontal scroll. Share your idea?
As per Android Documentation RecyclerView is the new way to organize the items in listview and to be displayed horizontally
Advantages:
Since by using Recyclerview Adapter, ViewHolder pattern is
automatically implemented
Animation is easy to perform
Many more features
More Information about RecyclerView:
grokkingandroid.com
antonioleiva.com
Sample:
survivingwithandroid.com
Just add the below block to make the ListView to horizontal from vertical
Code-snippet
LinearLayoutManager layoutManager= new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL, false);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(layoutManager);
Paul doesn't bother to fix bugs of his library or accept users fixes. That's why I am suggesting another library which has similar functionality:
https://github.com/sephiroth74/HorizontalVariableListView
Update: on Jul 24, 2013 author (sephiroth74) released completely rewritten version based on code of android 4.2.2 ListView. I must say that it doesn't have all the errors which previous version had and works great!
#Paul answer links to a great solution, but the code doesn't allow to use onClickListeners on items children (the callback functions are never called). I've been struggling for a while to find a solution and I've decided to post here what you need to modify in that code (in case somebody need it).
Instead of overriding dispatchTouchEvent override onTouchEvent. Use the same code of dispatchTouchEvent and delete the method (you can read the difference between the two here http://developer.android.com/guide/topics/ui/ui-events.html#EventHandlers )
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = mGesture.onTouchEvent(event);
return handled;
}
Then, add the following code which will decide to steal the event from the item children and give it to our onTouchEvent, or let it be handled by them.
#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);
}
}
Finally, don't forget to declare the variables in your class:
private float mInitialX;
private float mInitialY;
Since Google introduced Android Support Library v7 21.0.0, you can use RecyclerView to scroll items horizontally. The RecyclerView widget is a more advanced and flexible version of ListView.
To use RecyclerView, just add dependency:
com.android.support:recyclerview-v7:23.0.1
Here is a sample:
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);
}
}
More info about RecyclerView:
https://developer.android.com/training/material/lists-cards.html
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
This is a little (very) late, but I'm posting this in case someone comes by this later.
The Support Library as of the Android L preview has a RecyclerView that does exactly what you want.
Right now, you can only get it through the L preview SDK and you need to set your minSdk to L. But you can copy all of the necessary files into your project and use them that way until L is officially out.
You can download the preview docs here.
Warning: The API for Recycler View may change and it may have bugs.
Updated
The source code for horizontal listview is:
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
RecyclerView myList = findViewById(R.id.my_recycler_view);
myList.setLayoutManager(layoutManager);
Download the jar file from here
now put it into your libs folder, right click it and select 'Add as library'
now in main.xml put this code
<com.devsmart.android.ui.HorizontalListView
android:id="#+id/hlistview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
now in Activity class if you want Horizontal Listview with images then put this code
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;
}
Its actually very simple:
simply Rotate the list view to lay on its side
mlistView.setRotation(-90);
Then upon inflating the children, that should be inside the getView method. you rotate the children to stand up straight:
mylistViewchild.setRotation(90);
Edit:
if your ListView doesnt fit properly after rotation, place the ListView inside this RotateLayout like this:
<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>
My solution is to simply use ViewPager widget. It isn't center-locked as Gallery and has a built-in features for recycling views (as ListView). You may see similar approach at Google Play app, whenever you deal with horizontally scrollable lists.
You just need to extend PagerAdapter and perform a couple of tweaks there:
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;
}
}
As result, you'll have horizontally scrollable widget with adapter, like this:
Note: Android now supports horizontal list views using RecyclerView, so now this answer is deprecated, for information about RecyclerView :
https://developer.android.com/reference/android/support/v7/widget/RecyclerView
I have developed a logic to do it without using any external horizontal scrollview library, here is the horizontal view that I achieved and I have posted my answer here:https://stackoverflow.com/a/33301582/5479863
My json response is this:
{"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"}]}}
Layout file :
<?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>
class file:
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;
}
trick that is working here is the id that I have assigned to ImageView "imgView.setId(v)" and after that applying onClickListener to that I am again fetching the id of the view....I have also commented inside the code so that its easy to understand,
I hope this may be very useful...
Happy Coding... :)
This isn't much of an answer, but how about using a Horizontal Scroll View?
You can use RecyclerView in the support library. RecyclerView is a generalized version of ListView that supports:
A layout manager for positioning items
Default animations for common
item operations
Android Recycler View Docs
I've done a lot of searching for a solution to this problem. The short answer is, there is no good solution, without overriding private methods and that sort of thing. The best thing I found was to implement it myself from scratch by extending AdapterView. It's pretty miserable. See my SO question about horizontal ListViews.
I had to do the same for one of my projects and I ended up writing my own as well. I called it HorzListView is now part of my open source Aniqroid library.
http://aniqroid.sileria.com/doc/api/ (Look for downloads at the bottom or use google code project to see more download options: http://code.google.com/p/aniqroid/downloads/list)
The class documentation is here: http://aniqroid.sileria.com/doc/api/com/sileria/android/view/HorzListView.html
For my application, I use a HorizontalScrollView containing LinearLayout inside, which has orientation set to horizontal. In order to add images inside, I create ImageViews inside the activity and add them to my LinearLayout. For example:
<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>
An this works perfectly fine for me. In the activity all I have to do is something like the code below:
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);
As I said that works for me, and I hope it helps somebody looking to achieve this as well.
well you can always create your textviews etc dynamically and set your onclicklisteners like you would do with an adapter
HorizontialListView can't work when the data in the adapter is involved in another thread. Everything runs 100% on UI thread.This is a big problem in multithread. I think using HorizontialListView is not the best solution for your problem.HorzListView is a better way.You just replace your previous Gallery with HorzListView.You neednot modify the code about the adapter.Then everything goes the way you hope.See https://stackoverflow.com/a/12339708/1525777 about HorzListView.
I had used horizontal listview link in my project & I got good results. I had been used devsmart library initially but it gave me some issues. So best way to use horizontal listview link as it recovered my issues & also I recently launched my app on Google PlayStore using this library & got nice response from users. So I recommend you to use the same library which I mentioned above to show listview horizontally. Enjoy :)
There is a great library for that, called TwoWayView, it's very easy to implement, just include the project library into your work space and add it as a library project to your original project, and then follow the following steps which are originally mentioned here:
First, let's add a style indicating the orientation of the ListView
(horizontal or vertical) in (res/values/styles.xml):
<style name="TwoWayView">
<item name="android:orientation">horizontal</item>
</style>
Then,
In your Layout XML, use the following code to add the 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" />
and finally, just declare it and deal with it like any regular ListView:
TwoWayView lvTest = (TwoWayView) findViewById(R.id.lvItems);
All the methods of ListView will work here as usual, but there is only one difference I noticed, which is when setting the choice mode, the method setChoiceMode not takes an int value but a value from enum called ChoiceMode, so list_view.setChoiceMode(ListView.CHOICE_MODE_SINGLE); will be lvTest.setChoiceMode(ChoiceMode.SINGLE); // or MULTIPLE or NONE.
You may use ViewFlipper to include the layout XML and add images , listview for each layout XML

Categories

Resources