My android layout with grid view is not showing properly on 480 x 854 pixels (~196 ppi pixel density) device.and please suggest me is there any method to work with layout creation at run time.
Android categorizes device screens using two general properties: size
and density. You should expect that your app will be installed on
devices with screens that range in both size and density.
Create Different Layouts
To optimize your user experience on different screen sizes, you should create a unique layout XML file for each screen size you want to support. Each layout should be saved into the appropriate resources directory .
More info you can visit official guideline
Supporting Different Screen Sizes
#N.shah , For Gridview, You should make all view dynamic, So that all the cells come exactly on each and every device
How We can do this
Like we have 4 rows and 2 columns
All 4 rows should show on window
1. Find out the device width and height
2. Lets say height is X. Find out height of status bar i.e Y and action bar Z
3. Find out the exact height where we have to show our gridview i.e
X=X-(Y+Z)
4. Divide the X by no. of rows you want to show to find out row height i.e R
R=X/4( where 4 is no. of rows)
5. Create an xml which you want to show in gridview
custom_gridview
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/ll_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_marginBottom="#dimen/menu_grid_vertical_spacing"
android:orientation="vertical"
android:weightSum="4.5" >
<View
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.9"
android:gravity="center" >
<ImageView
android:id="#+id/img_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:padding="#dimen/menu_page_padding"
android:src="#drawable/ic_launcher" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.4" />
<com.slinfy.ikharelimiteduk.custom.CustomTextViewSegoeUISemiBold
android:id="#+id/txt_name"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.2"
android:gravity="top|center_horizontal"
android:text="#string/app_name"
android:textColor="#android:color/white"
android:textSize="#dimen/app_menu_text_size" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="#dimen/menu_grid_vertical_spacing"
android:layout_alignParentBottom="true"
android:background="#2E3A60" />
</RelativeLayout>
Make an adapter
GridViewAdapter
import com.slinfy.ikharelimiteduk.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
#SuppressLint({ "ViewHolder", "InflateParams" })
public class GridViewAdapter extends BaseAdapter {
String[] mMenuNames;
int[] mColors;
Integer[] mMenuIcons;
Context mContext;
private LayoutInflater inflater;
float mGridSize;
float mPadding;
int noOfColums = 4;
public GridViewAdapter(String[] menuNames, Context context, int[] colors, Integer[] menuIcons, float gridSize) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mMenuNames = menuNames;
mMenuIcons = menuIcons;
mColors = colors;
mContext = context;
mGridSize = gridSize;
mPadding = context.getResources().getDimension(R.dimen.menu_grid_vertical_spacing);
}
#Override
public int getCount() {
return mMenuIcons.length;
}
#Override
public Object getItem(int arg0) {
return null;
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(int index, View arg1, ViewGroup arg2) {
ViewHolder holder = null;
if (arg1 == null) {
holder = new ViewHolder();
arg1 = inflater.inflate(R.layout.custom_gridview, null);
holder.imgMenu = (ImageView) arg1.findViewById(R.id.img_menu);
holder.txtName = (TextView) arg1.findViewById(R.id.txt_name);
holder.layout = (RelativeLayout) arg1.findViewById(R.id.layout);
arg1.setTag(holder);
} else {
holder = (ViewHolder) arg1.getTag();
}
holder.txtName.setText(mMenuNames[index]);
holder.imgMenu.setImageResource(mMenuIcons[index]);
holder.layout.setBackgroundResource(mColors[index]);
holder.layout.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT,
(int) ((mGridSize / noOfColums) - 5/* - (mPadding) - 6 */)));
return arg1;
}
class ViewHolder {
TextView txtName;
ImageView imgMenu;
RelativeLayout layout;
}
}
Set the adapter in your main class
GridView grid = (GridView) findViewById(R.id.grid_view);
grid.setAdapter(new GridViewAdapter(menuNames, getApplicationContext(), colors, menuIcons, gridSize));
Related
I am currently working on rewriting one of my iOS apps to release on Android. I'm making good progress but I am looking for some guidance regarding the best way to approach the list rows.
These are the cells/rows I am trying to recreate:
As you can see, I have a white background view with padding around each edge and rounded corners, so I guess for this I would need to embed everything within a view of some sort? The other part I am unsure about is how to create the coloured circle on the right. Would this be another view with rounded corners and a coloured background? I haven't managed to figure out how to get this to the right of the two textViews that I currently have. So if anyone could give a code example then that would be great.
This is my current XML:
<ImageView
android:layout_width="78dp"
android:layout_height="78dp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:scaleType="centerCrop"
android:adjustViewBounds="false"
android:id="#+id/attractionImageView"
android:contentDescription="Attraction Image"
android:background="#color/colorPrimary" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:background="#drawable/fast_pass"
android:id="#+id/fastPassImageView" />
<LinearLayout android:orientation="vertical" android:layout_height="match_parent" android:layout_width="fill_parent" android:layout_toEndOf="#+id/attractionImageView" android:layout_toRightOf="#+id/attractionImageView">
<TextView
android:layout_width="match_parent"
android:layout_height="49dp"
android:id="#+id/attractionNameTextView"
android:text="Attraction Name"
android:layout_marginLeft="5dp"/>
<LinearLayout android:orientation="horizontal" android:layout_height="match_parent" android:layout_width="fill_parent" android:layout_toEndOf="#+id/attractionImageView" android:layout_toRightOf="#+id/attractionImageView">
<ImageView
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:id="#+id/updatedImageView"
android:background="#drawable/updated"
android:layout_gravity="center_vertical"/>
<TextView
android:layout_width="match_parent"
android:layout_height="29dp"
android:id="#+id/updatedTextView"
android:text="Updated"
android:gravity="center_vertical"/>
</LinearLayout>
</LinearLayout>
This seems to work well for everything I've got so far, but I'm not sure where to go from here. This is how it looks:
Any suggestions?
For the colored circle- I'd suggest just a text view set to the proper size with a circle drawable set as the background of the view. Should get what you want.
For the background- I'd just stick the entire row in either a linear or relative layout, then set a RoundedBitmapDrawable as the background of the layout. That will give you the rounded background effect. If necessary add some margin to the top and bottom of each view to increase the gap between items.
I suppose that you're using custom views for your listview's items, if so, I would set the background of the custom view to something like this:
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#color/white" />
<stroke android:width="4dp" android:color="#color/beige" /> //If you want a stroke
<corners
android:topLeftRadius="20dp"
android:topRightRadius="20dp"
android:bottomLeftRadius="20dp"
android:bottomRightRadius="20dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
As an alternate solution, I just got something working with CardViews and using Picasso with a CircleTransformation for the circle.
In this simple example, I used a RecyclerView of CardViews.
First, here is the CircleTransformation class that is used by Picasso:
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import com.squareup.picasso.Transformation;
public class CircleTransform implements Transformation {
#Override
public Bitmap transform(Bitmap source) {
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
Canvas canvas = new Canvas(bitmap);
BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);
paint.setShader(shader);
float r = size/2f;
canvas.drawCircle(r, r, r-1, paint);
Paint paintBorder = new Paint();
paintBorder.setStyle(Style.STROKE);
paintBorder.setColor(Color.argb(84,0,0,0));
paintBorder.setAntiAlias(true);
paintBorder.setStrokeWidth(1);
canvas.drawCircle(r, r, r-1, paintBorder);
squaredBitmap.recycle();
return bitmap;
}
#Override
public String key() {
return "circle";
}
}
Here is the Fragment:
public class BlankFragment extends Fragment {
public BlankFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_blank, container, false);
RecyclerView rv = (RecyclerView) rootView.findViewById(R.id.rv_recycler_view);
rv.setHasFixedSize(true);
MyAdapter adapter = new MyAdapter(new String[]{"testone", "testtwo", "testthree", "testfour"}, getActivity());
rv.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
return rootView;
}
}
fragment_blank.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="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
The adapter, which includes the defaultCircleWithText() method for drawing the circle and text. Here I'm just using one color for the circles, but you can extend this to set the correct circle color for each row:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] mDataset;
Context mContext;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class MyViewHolder extends RecyclerView.ViewHolder {
public CardView mCardView;
public TextView mTextView;
public ImageView mImageView;
public MyViewHolder(View v) {
super(v);
mCardView = (CardView) v.findViewById(R.id.card_view);
mTextView = (TextView) v.findViewById(R.id.tv_text);
mImageView = (ImageView) v.findViewById(R.id.iv_image);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(String[] myDataset, Context context) {
mDataset = myDataset;
mContext = context;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_item, parent, false);
// set the view's size, margins, paddings and layout parameters
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(mDataset[position]);
Drawable drawable = new BitmapDrawable(mContext.getResources(), defaultCircleWithText("test"));
Picasso.with(mContext).load((String)null).fit().transform(new CircleTransform()).placeholder(drawable).into(holder.mImageView);
}
#Override
public int getItemCount() {
return mDataset.length;
}
public final static int BIG_IMAGE = 138;
public static Bitmap defaultCircleWithText(String text) {
int size = BIG_IMAGE;
Bitmap image = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(image);
int alpha = 165;
//hard-coded circle color:
int color = Color.argb(alpha,253,70,45);
Paint p_circle = new Paint();
p_circle.setAntiAlias(true);
p_circle.setColor(color);
c.drawCircle(size/2f, size/2f, size/2f-1, p_circle);
Paint p_text = new Paint();
p_text.setAntiAlias(true);
p_text.setColor(Color.WHITE);
p_text.setTextSize(58);
RectF bounds = new RectF(0, 0, c.getWidth(), c.getHeight());
// measure text width
bounds.right = p_text.measureText(text, 0, text.length());
// measure text height
bounds.bottom = p_text.descent() - p_text.ascent();
bounds.left += (c.getWidth() - bounds.right) / 2.0f;
bounds.top += (c.getHeight() - bounds.bottom) / 2.0f;
c.drawText(text, bounds.left, bounds.top - p_text.ascent(), p_text);
return image;
}
}
card_item.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="78dp" >
<android.support.v7.widget.CardView
android:id="#+id/card_view"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
card_view:cardCornerRadius="4dp"
android:layout_width="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="test" >
</TextView>
<ImageView
android:id="#+id/iv_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
Result:
I have a listfragment which returns a list of views from an arrayadapter. I wanted to know if its possible to have other views (a spinner and textview, which are in a seperate layout file) above the listfragment in the activty. How would I need to change the layout files for to achieve this?
rowlayout file xml for adapter:
<?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="fill_parent"
android:orientation="vertical" >
<!--Rank number-->
<TextView
android:id="#+id/rank"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#+id/label"
android:textSize="40px"
/>
<!-- -->
<!--Device image-->
<ImageView
android:id="#+id/icon"
android:layout_width="60px"
android:layout_height="60px"
android:layout_marginLeft="15px"
android:layout_marginRight="10px"
android:layout_marginTop="4px"
android:layout_toRightOf="#+id/rank"
android:src="#drawable/ic_launcher" />
<!--Caption-->
<TextView
android:id="#+id/caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#+id/label"
android:layout_below="#+id/icon"
android:layout_marginLeft="25px"
/>
</RelativeLayout>
layout file topview.xml containing views to be placed above:
<?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" >
<!--Category text-->
<TextView
android:id="#+id/text_chooseCategory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<!--drop down-->
<Spinner
android:id="#+id/list_cateory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Here is an arrayadapter class:
package com.example.listfragmentexample;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class SimpleArrayAdapter extends ArrayAdapter<String> {
//arrays and context
//call it to get information regarding another part of your program (activity, package/application)
Context context;
String ranks[];
String caption[] = new String[] {"LG G2", "HTC One", "Google Nexus 5", "iPhone 5s",
"Sony Xperia Z1 Compact", "Samsung Galaxy S4", "Samsung Galaxy Note 3", "Motorola Moto G", "BlackBerry Q10",
"Nokia Lumia 1020"};
int images[] = new int[] {R.drawable.ic_launcher};
//viewholder class
public class ViewHolder {
TextView rankView;
ImageView imageView;
TextView captionView;
}
public SimpleArrayAdapter(Context context, String[] ranks) {
super(context, R.layout.rowlayout, ranks);
this.context = context;
this.ranks = ranks;
}
//return multiple views
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//Layout inflater instantiates XML layout file
//get system service.
LayoutInflater inflater = (LayoutInflater) context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewHolder holder = new ViewHolder();
//get row view. xml file and root (parent)
//used with layout inflater
View rootView = inflater.inflate(R.layout.rowlayout, parent, false);
holder.rankView = (TextView) rootView.findViewById(R.id.rank);
holder.imageView = (ImageView) rootView.findViewById(R.id.icon);
holder.captionView = (TextView) rootView.findViewById(R.id.caption);
//set the data
holder.rankView.setText(ranks[position]);
/*holder.imageView.setImageResource(R.drawable.ic_launcher);*/
holder.captionView.setText(caption[position]);
return rootView;
}
}
The adapter is set in a listfragment class, which itself is started in another activity.
Happy to clarify and thanks in advance.
You can inflate a custom layout to your ListFragment, as it is described in ListView documentation, Class Overview section. This custom layout must contain a ListView with the id "#android:id/list" and other controls you need (such as Spinner, TextView and so on).
EDIT: This problem has been resolved, so the code in my original post does not apply anymore. Thanks to everyone who contributed.
First off, I have checked other similar questions but they either don't match mine or use the BaseAdapter. What I'm trying to do is display a text-image combination as part of a GridView. The actual app is larger, but my problem is that the GridView is blank in the very first activity!
I'd appreciate if someone can take a quick look at it and tell me what's wrong.
Code for layout file used for GridView elements:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageView
android:id="#+id/iv_grid"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/tv_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
Layout file of first Activity:
<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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".CategoriesActivity" >
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="54dp"
android:text="Select a category to learn more" />
<TextView
android:id="#+id/tv_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="26dp"
android:text="Lessons from History"
android:textSize="18sp" />
<GridView
android:id="#+id/gridView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/textView2"
android:layout_marginTop="25dp"
android:numColumns="3" >
</GridView>
</RelativeLayout>
And finally, the Java file:
package com.example.historicpersonalities;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
public class CategoriesActivity extends Activity {
GridView gv_categories;
public int selected; //which category is selected [0-4]?
String[] data; //holds the data for GridView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_categories);
populate();
selected = -1;
gv_categories = (GridView) findViewById(R.id.gridView1);
gv_categories.setAdapter(new MyAdapter(this,
android.R.layout.simple_expandable_list_item_1));
gv_categories.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index,
long arg3) {
}
});
}
class MyAdapter extends ArrayAdapter<String> {
public MyAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.layout_grid_scheme, null);
TextView item = (TextView) findViewById(R.id.tv_grid);
item.setText(data[position]);
ImageView img = (ImageView) findViewById(R.id.iv_grid);
img.setImageResource(R.drawable.dummy);
Log.d("ankush", ""+position);
return v;
}
}
private void populate() {
data = new String[5];
data[0] = "Writers";
data[1] = "Painters";
data[2] = "Conquerors";
data[3] = "Chemists";
data[4] = "Actors";
}
}
I think the problem is the height of your gridView. GridView is a scrollable container and can contain any amount of content in any height. Try to set background color to your gridView and you will see, that there is no gridview on the screnn.
It's because you use wrap_content for gridview, but it's incorrect.
If I'm not wrong, it seems that in your constructor you don't pass any data to the adapter.
public MyAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
In this code snippet, your are using ArrayAdapter (Context context, int textViewResourceId) which initialize an empty arraylist as the objects to represent in the GridView.
public ArrayAdapter(Context context, int textViewResourceId) {
init(context, textViewResourceId, 0, new ArrayList<T>());
}
Therefore your GridView should be empty. In addition to that, it seem you didn't properly implement the getView.(findViewById() parts should be handled as shown in the Opiatefuchs's answer)
If you want to extend ArrayAdapter, you should be calling a constructor which takes an array of objects as a parameter. (Of course there are many ways to make a custom adapter you can extend BaseAdapter or while extending ArrayAdapter you can override some other methods)
This way it should work:
class MyAdapter extends ArrayAdapter<String> {
public MyAdapter(Context context, int textViewResourceId, String[] objects) {
super(context, textViewResourceId, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.layout_grid_scheme, null);
} else {
view = convertView;
}
TextView item = (TextView) view.findViewById(R.id.tv_grid);
item.setText(getItem(position));
ImageView img = (ImageView) view.findViewById(R.id.iv_grid);
img.setImageResource(R.drawable.dummy);
}
}
Don´t know if this causes the problem, but You have to set Your TextView and ImageView in Your adapter like this:
TextView item = (TextView) v.findViewById(R.id.tv_grid);
item.setText(data[position]);
ImageView img = (ImageView) v.findViewById(R.id.iv_grid);
img.setImageResource(R.drawable.dummy);
EDIT
First of all, the best way is to combine all three solutions. BUT, I have seen another issue in Your xml layout: You put the same id to the TextView inside Your GridView Element xml and Your Activity layout xml. Both TextViews got id="#+id/tv_grid" . This could never work, change the id´s to different ones
I really want to implement this (the side navigation) in an app of my own, does anyone know how Google managed to do this?
They seem to have pulled the current window aside and put in a fly-in navigation of their own.
In fact, there's a way to do this. Even without implementing your own ActionBar.
Just have a look at the hierachyviewer! (Located in the tools directory)
There's the DecorView, and a LinearLayout as a child. This LinearLayout contains both the ActionBar and the other content. So, you can simply apply some FrameLayout.LayoutParams to this LinearLayout and get some space on the left side this way. Then, you can fill this space with your menu-ListView and overlay the other content with a FrameLayout, that, when it's clicked, collapses the menu. So, here's some code:
First, the class for collapsing / expanding (SlideMenu.java):
package your.cool.app;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
public class SlideMenu {
//just a simple adapter
public static class SlideMenuAdapter extends ArrayAdapter<SlideMenu.SlideMenuAdapter.MenuDesc> {
Activity act;
SlideMenu.SlideMenuAdapter.MenuDesc[] items;
class MenuItem {
public TextView label;
public ImageView icon;
}
static class MenuDesc {
public int icon;
public String label;
}
public SlideMenuAdapter(Activity act, SlideMenu.SlideMenuAdapter.MenuDesc[] items) {
super(act, R.id.menu_label, items);
this.act = act;
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = act.getLayoutInflater();
rowView = inflater.inflate(R.layout.menu_listitem, null);
MenuItem viewHolder = new MenuItem();
viewHolder.label = (TextView) rowView.findViewById(R.id.menu_label);
viewHolder.icon = (ImageView) rowView.findViewById(R.id.menu_icon);
rowView.setTag(viewHolder);
}
MenuItem holder = (MenuItem) rowView.getTag();
String s = items[position].label;
holder.label.setText(s);
holder.icon.setImageResource(items[position].icon);
return rowView;
}
}
private static boolean menuShown = false;
private static View menu;
private static LinearLayout content;
private static FrameLayout parent;
private static int menuSize;
private static int statusHeight = 0;
private Activity act;
SlideMenu(Activity act) {
this.act = act;
}
//call this in your onCreate() for screen rotation
public void checkEnabled() {
if(menuShown)
this.show(false);
}
public void show() {
//get the height of the status bar
if(statusHeight == 0) {
Rect rectgle = new Rect();
Window window = act.getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
statusHeight = rectgle.top;
}
this.show(true);
}
public void show(boolean animate) {
menuSize = Functions.dpToPx(250, act);
content = ((LinearLayout) act.findViewById(android.R.id.content).getParent());
FrameLayout.LayoutParams parm = (FrameLayout.LayoutParams) content.getLayoutParams();
parm.setMargins(menuSize, 0, -menuSize, 0);
content.setLayoutParams(parm);
//animation for smooth slide-out
TranslateAnimation ta = new TranslateAnimation(-menuSize, 0, 0, 0);
ta.setDuration(500);
if(animate)
content.startAnimation(ta);
parent = (FrameLayout) content.getParent();
LayoutInflater inflater = (LayoutInflater) act.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
menu = inflater.inflate(R.layout.menu, null);
FrameLayout.LayoutParams lays = new FrameLayout.LayoutParams(-1, -1, 3);
lays.setMargins(0,statusHeight, 0, 0);
menu.setLayoutParams(lays);
parent.addView(menu);
ListView list = (ListView) act.findViewById(R.id.menu_listview);
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//handle your menu-click
}
});
if(animate)
menu.startAnimation(ta);
menu.findViewById(R.id.overlay).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
SlideMenu.this.hide();
}
});
Functions.enableDisableViewGroup((LinearLayout) parent.findViewById(android.R.id.content).getParent(), false);
((ExtendedViewPager) act.findViewById(R.id.viewpager)).setPagingEnabled(false);
((ExtendedPagerTabStrip) act.findViewById(R.id.viewpager_tabs)).setNavEnabled(false);
menuShown = true;
this.fill();
}
public void fill() {
ListView list = (ListView) act.findViewById(R.id.menu_listview);
SlideMenuAdapter.MenuDesc[] items = new SlideMenuAdapter.MenuDesc[5];
//fill the menu-items here
SlideMenuAdapter adap = new SlideMenuAdapter(act, items);
list.setAdapter(adap);
}
public void hide() {
TranslateAnimation ta = new TranslateAnimation(0, -menuSize, 0, 0);
ta.setDuration(500);
menu.startAnimation(ta);
parent.removeView(menu);
TranslateAnimation tra = new TranslateAnimation(menuSize, 0, 0, 0);
tra.setDuration(500);
content.startAnimation(tra);
FrameLayout.LayoutParams parm = (FrameLayout.LayoutParams) content.getLayoutParams();
parm.setMargins(0, 0, 0, 0);
content.setLayoutParams(parm);
Functions.enableDisableViewGroup((LinearLayout) parent.findViewById(android.R.id.content).getParent(), true);
((ExtendedViewPager) act.findViewById(R.id.viewpager)).setPagingEnabled(true);
((ExtendedPagerTabStrip) act.findViewById(R.id.viewpager_tabs)).setNavEnabled(true);
menuShown = false;
}
}
Some helping methods (for me, in static Functions.java):
public static int dpToPx(int dp, Context ctx) {
Resources r = ctx.getResources();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
}
//originally: http://stackoverflow.com/questions/5418510/disable-the-touch-events-for-all-the-views
//modified for the needs here
public static void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
if(view.isFocusable())
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
} else if (view instanceof ListView) {
if(view.isFocusable())
view.setEnabled(enabled);
ListView listView = (ListView) view;
int listChildCount = listView.getChildCount();
for (int j = 0; j < listChildCount; j++) {
if(view.isFocusable())
listView.getChildAt(j).setEnabled(false);
}
}
}
}
Then, the layouts:
Layout of the menu (res/layout/menu.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="250dip"
android:background="#color/darkblack">
<ListView
android:id="#+id/menu_listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="#color/dividerblack"
android:dividerHeight="2dip" />
</LinearLayout>
<FrameLayout
android:id="#+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
Layout of the listitems (res/layout/menu_listitem.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="fill_parent" >
<ImageView
android:id="#+id/menu_icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="5dip"
android:layout_marginLeft="10dip"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip" />
<TextView
android:id="#+id/menu_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/white"
android:textSize="24dp"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip" />
</LinearLayout>
How to use it:
In your onCreate():
private SlideMenu slidemenu;
#Override
public void onCreate(Bundle savedInstanceState) {
//your onCreate code
slidemenu = new SlideMenu(this);
slidemenu.checkEnabled();
}
In the handler for your ActionBar homebutton:
slidemenu.show();
That's it!
And now, a little screenshot of it in action:
As far as I know, it is working. If you experience any problems or my explanations are not clear, please contact me!
EDIT: ExtendedViewPager & ExtendedPagerStrip:
ExtendedViewPager:
package your.cool.app;
//source: http://blog.svpino.com/2011/08/disabling-pagingswiping-on-android.html
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class ExtendedViewPager extends ViewPager {
private boolean enabled;
public ExtendedViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.enabled = true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onTouchEvent(event);
}
return false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onInterceptTouchEvent(event);
}
return false;
}
public void setPagingEnabled(boolean enabled) {
this.enabled = enabled;
}
}
ExtendedPagerTabStrip:
package your.cool.app;
//source: http://blog.svpino.com/2011/08/disabling-pagingswiping-on-android.html
import android.content.Context;
import android.support.v4.view.PagerTabStrip;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class ExtendedPagerTabStrip extends PagerTabStrip {
private boolean enabled;
public ExtendedPagerTabStrip(Context context, AttributeSet attrs) {
super(context, attrs);
this.enabled = true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onTouchEvent(event);
}
return false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onInterceptTouchEvent(event);
}
return false;
}
public void setNavEnabled(boolean enabled) {
this.enabled = enabled;
}
}
I use this SlideMenu for an Activity with a ViewPager with PagerTabStrip for tabs like Talk, Market etc. You can't disable these Views in an easy way, so the two classes above just extend them to stop the onTouch event when disabled.
There are several attempts at doing this, however I have yet to find a lib or source code on how to implement it successfully with actionbar accross all api levels. One promising lib is here
https://github.com/jfeinstein10/SlidingMenu
here is a video of the example app.
here is the Google Play app link.
This does work with ActionbarSherlock. You will have to build the SlidingMenu library with ABS to get it working. Works and looks great!
Did a roundup of the original implementation and added XML parsing as well as autodetection of a possibly present actionbar, so it works with the native as well as a support action bar such as ActionBarSherlock.
The whole thing is now a library project together with an example app and is described over at Sliding Menu for android Thanks to scirocco for the initial idea and code!
If you are using API level greater that 11 you can use a much simpler approach inspired by the answer given by #Scirocco
// get content parent that is basically the whole
// app screen (viewed from hierarchy viewer)
final LinearLayout content =
(LinearLayout) findViewById(android.R.id.content).getParent();
// make new value animator with range from 0 to 1
final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
// set custom duration
animator.setDuration(500);
// on update is called for every value in the
// given range in time frame defined by the duration
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
// get the current value
float value = ((Float) (animation.getAnimatedValue())).floatValue();
// translate by that value, minus means translate left
content.setTranslationX(-250 * value);
}
});
// start the animator
animator.start();
// make or inflate custom view for test purposes
Button textView = new Button(this);
textView.setText("TestButton");
// add it to the frame layout that is the parent of the content on position 0
FrameLayout parent = (FrameLayout) content.getParent();
parent.addView(textView, 0);
The idea here is to use ValueAnimator that transforms and not just animates the main layout with the Action bar, so you can interact with the inflated view you want to use as a sliding panel.
You should replace the hardcoded values with something that is of use to your app.
I hope this helps :)
Well currently im working on a project and came across Sliding menu,i googled but gets very disappointed to see that no one has given some piece of code or some hint for how to start making a sliding menu,but every one has given link to some github's projects/libraries to use,I decided to do it myself and finally i have my own Sliding Menu Ready...
I have Spent two days on it
1. on making animations of sliding
2. on making it work with all screen resolutions
Its really easy and simple once you get some idea about Animations, i have read some where,its not sensible to re-invent the Wheel(people who are refering to github source code of sliding menu),but i beleif that you should atleast once try to make your own so you get a idea how it actually works and functions :P
so this is a picture of how my sliding menu will going to work
1.Find.xml //later in the code it will be refer as findLayout
<?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="match_parent" >
<RelativeLayout
android:id="#+id/find_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="60dp"
android:padding="2dp"
android:background="#drawable/main_header">
<Button
android:id="#+id/filter"
android:layout_width="40dp"
android:layout_height="30dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="#drawable/filter_button" />
<TextView
android:id="#+id/city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/filter"
android:layout_marginLeft="20dp"
android:layout_marginTop="3dp"
android:text="Islamabad"
android:textSize="22sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_dark"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/city"
android:layout_alignLeft="#+id/city">
<TextView
android:id="#+id/interested_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Men and Women"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark"/>
<ImageView
android:id="#+id/separator"
android:layout_width="2dp"
android:layout_height="18dp"
android:layout_toRightOf="#+id/interested_in"
android:layout_marginLeft="4dp"
android:src="#drawable/separator_1"
android:layout_centerVertical="true" />
<TextView
android:id="#+id/age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_toRightOf="#+id/separator"
android:layout_centerVertical="true"
android:text="18-24 years"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark"/>
<ImageView
android:id="#+id/separator_1"
android:layout_width="2dp"
android:layout_height="18dp"
android:layout_toRightOf="#+id/age"
android:layout_marginLeft="4dp"
android:src="#drawable/separator_1"
android:layout_centerVertical="true" />
<TextView
android:id="#+id/distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_toRightOf="#+id/separator_1"
android:layout_centerVertical="true"
android:text=">30km"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark" />
</RelativeLayout>
</RelativeLayout>
<GridView
android:id="#+id/users_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/header"
android:numColumns="4">
</GridView>
</RelativeLayout>
<include
layout="#layout/filter"/> //here i included the filter.xml, which is on top of find.xml layout and is initially invisible
</RelativeLayout>
2.Filter.xml //later in code refer as FilterLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/filter_layout"
android:visibility="invisible"
android:layout_width="260dp"
android:layout_height="match_parent"
android:background="#drawable/grey_bg" >
<ImageView
android:id="#+id/profile_pic"
android:layout_width="match_parent"
android:layout_height="220dp"
android:src="#drawable/pic"/>
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="55dp"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:layout_below="#+id/profile_pic"
android:background="#drawable/light_blue_header">
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="Raja Babar"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_dark"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/name"
android:layout_alignLeft="#+id/name">
<TextView
android:id="#+id/gender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Male"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark" />
<ImageView
android:id="#+id/seperator"
android:layout_width="2dp"
android:layout_height="20dp"
android:layout_toRightOf="#+id/gender"
android:layout_marginLeft="5dp"
android:src="#drawable/separator_1"
android:layout_centerVertical="true" />
<TextView
android:id="#+id/age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/seperator"
android:layout_marginLeft="5dp"
android:layout_centerVertical="true"
android:text="22 years"
android:textSize="12sp"
android:textColor="#android:color/primary_text_dark" />
</RelativeLayout>
</RelativeLayout>
<ScrollView
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_below="#+id/header"
android:layout_marginTop="15dp"
android:layout_centerHorizontal="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/filter_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/filter_options"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<RelativeLayout
android:id="#+id/interested_in_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="40dp"
android:layout_below="#+id/filter_options"
android:background="#drawable/interested_in_field">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="#string/gender"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="#string/women_men"
android:textSize="18sp"
android:textColor="#33b9cd" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/age_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="40dp"
android:layout_below="#+id/interested_in_layout"
android:background="#drawable/age_field_1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="#string/age"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="18-24 years"
android:textSize="18sp"
android:textColor="#33b9cd"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingRight="40dp"
android:layout_below="#+id/age_layout"
android:background="#drawable/distance_field">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="#string/distance"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#android:color/primary_text_light"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text=">30km"
android:textSize="18sp"
android:textColor="#33b9cd"/>
</RelativeLayout>
</RelativeLayout>
</ScrollView>
</RelativeLayout>
In find.xml i have included filter.xml initially which is invisible
Now FilterAnimation.java
package matchat.helpers;
import com.s3.matchat.R;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.RelativeLayout;
public class FilterAnimation implements AnimationListener
{
Context context;
RelativeLayout filterLayout, otherLayout;
private Animation filterSlideIn, filterSlideOut, otherSlideIn, otherSlideOut;
private static int otherLayoutWidth, otherLayoutHeight;
private boolean isOtherSlideOut = false;
private int deviceWidth;
private int margin;
public FilterAnimation(Context context)
{
this.context = context;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
deviceWidth = displayMetrics.widthPixels; // as my animation is x-axis related so i gets the device width and will use that width,so that this sliding menu will work fine in all screen resolutions
}
public void initializeFilterAnimations(RelativeLayout filterLayout)
{
this.filterLayout = filterLayout;
filterSlideIn = AnimationUtils.loadAnimation(context, R.anim.filter_slide_in);
filterSlideOut = AnimationUtils.loadAnimation(context, R.anim.filter_slide_out);
}
public void initializeOtherAnimations(RelativeLayout otherLayout)
{
this.otherLayout = otherLayout;
otherLayoutWidth = otherLayout.getWidth();
otherLayoutHeight = otherLayout.getHeight();
otherSlideIn = AnimationUtils.loadAnimation(context, R.anim.other_slide_in);
otherSlideIn.setAnimationListener(this);
otherSlideOut = AnimationUtils.loadAnimation(context, R.anim.other_slide_out);
otherSlideOut.setAnimationListener(this);
}
public void toggleSliding()
{
if(isOtherSlideOut) //check if findLayout is already slided out so get so animate it back to initial position
{
filterLayout.startAnimation(filterSlideOut);
filterLayout.setVisibility(View.INVISIBLE);
otherLayout.startAnimation(otherSlideIn);
}
else //slide findLayout Out and filterLayout In
{
otherLayout.startAnimation(otherSlideOut);
filterLayout.setVisibility(View.VISIBLE);
filterLayout.startAnimation(filterSlideIn);
}
}
#Override
public void onAnimationEnd(Animation animation)
{
if(isOtherSlideOut) //Now here we will actually move our view to the new position,because animations just move the pixels not the view
{
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(otherLayoutWidth, otherLayoutHeight);
otherLayout.setLayoutParams(params);
isOtherSlideOut = false;
}
else
{
margin = (deviceWidth * 80) / 100; //here im coverting device percentage width into pixels, in my other_slide_in.xml or other_slide_out.xml you can see that i have set the android:toXDelta="80%",so it means the layout will move to 80% of the device screen,to work across all screens i have converted percentage width into pixels and then used it
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(otherLayoutWidth, otherLayoutHeight);
params.leftMargin = margin;
params.rightMargin = -margin; //same margin from right side (negavite) so that our layout won't get shrink
otherLayout.setLayoutParams(params);
isOtherSlideOut = true;
dimOtherLayout();
}
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationStart(Animation animation)
{
}
private void dimOtherLayout()
{
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f);
alphaAnimation.setFillAfter(true);
otherLayout.startAnimation(alphaAnimation);
}
}
Now Find.java
package main.matchat.activities;
import matchat.helpers.FilterAnimation;
import com.s3.matchat.R;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.View.OnClickListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.Button;
import android.widget.RelativeLayout;
public class Find extends Activity implements OnClickListener
{
RelativeLayout filterLayout, findLayout;
Button btFilter;
FilterAnimation filterAnimation;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.find);
filterLayout = (RelativeLayout)findViewById(R.id.filter_layout);
findLayout = (RelativeLayout)findViewById(R.id.find_layout);
btFilter = (Button)findViewById(R.id.filter);
btFilter.setOnClickListener(this);
filterAnimation = new FilterAnimation(this);
initializeAnimations();
}
private void initializeAnimations()
{ //Setting GlobolLayoutListener,when layout is completely set this function will get called and we can have our layout onbject with correct width & height,else if you simply try to get width/height of your layout in onCreate it will return 0
final ViewTreeObserver filterObserver = filterLayout.getViewTreeObserver();
filterObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
filterLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int deviceWidth = displayMetrics.widthPixels;
int filterLayoutWidth = (deviceWidth * 80) / 100; //here im coverting device percentage width into pixels, in my other_slide_in.xml or other_slide_out.xml you can see that i have set the android:toXDelta="80%",so it means the layout will move to 80% of the device screen,to work across all screens i have converted percentage width into pixels and then used it
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(filterLayoutWidth, RelativeLayout.LayoutParams.MATCH_PARENT);
filterLayout.setLayoutParams(params);//here im setting the layout params for my filter.xml because its has width 260 dp,so work it across all screen i first make layout adjustments so that it work across all screens resolution
filterAnimation.initializeFilterAnimations(filterLayout);
}
});
final ViewTreeObserver findObserver = findLayout.getViewTreeObserver();
findObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
findLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
filterAnimation.initializeOtherAnimations(findLayout);
}
});
}
#Override
public void onClick(View v)
{
int id = v.getId();
switch(id)
{
case R.id.filter:
filterAnimation.toggleSliding();
break;
}
}
}
Here are the animations res/anim
1.filter_slide_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="-100%"
android:toXDelta="0%"
android:duration="1000"
android:fillEnabled="true" />
</set>
2.filter_slide_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%"
android:toXDelta="-100%"
android:duration="1000"/>
</set>
3.other_slide_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator" >
<translate
android:fromXDelta="0%"
android:toXDelta="-80%"
android:duration="1000"
android:fillEnabled="true"/>
</set>
4.other_slide_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%"
android:toXDelta="80%"
android:duration="1000"
android:fillEnabled="true"/>
</set>
There you go a complete working and functional Sliding Menu, and you can customized it to meet your requirements,if any one still have some problems setting up,feel free to ask,i feel pleasure to help you out :)
I've created my own solution for sliding away the view and revealing a menu underneath, as many other solutions appeared to not work on older Android versions or lacked proper instructions on how to get it to work.
My solution has the following features:
Provides support for sliding away a view to reveal a menu that lies underneath it
Both the menu and the view above can be any custom View
Supported on old Android versions (tested to work at least on Android 2.2)
Works with PhoneGap / Cordova projects
The solution uses a custom layout, called SlidingMenuLayout, that you are expected to add 2 views to. The first view you add is the menu, the second is the main view.
The simplest way to add the layout to your existing project is to override your Activity's setContentView() method:
#Override
public void setContentView(View view) {
SlidingMenuLayout layout = new SlidingMenuLayout(this);
layout.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
0.0F));
layout.addView(new MenuView(this));
layout.addView(view);
super.setContentView(layout);
}
In this example, MenuView is the view that will actually show the menu. It is up to you to implement this view.
Finally, you can add a button (typically in the top left corner of your main view), that calls openMenu() or closeMenu() on the layout as appropriate.
The code for SlidingMenuLayout is found on the GitHub project page.
For those of you who uses the SlidingMenu library (https://github.com/jfeinstein10/SlidingMenu) there is a way to jack it in and it seems to work! With help of #Scirocco put this in your onCreate for the activity:
ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
mSlidingMenu = new SlidingMenu(this);
ViewGroup mainContent = (ViewGroup) decorView.getChildAt(0);
decorView.removeView(mainContent);
mSlidingMenu.setContent(mainContent);
decorView.addView(mSlidingMenu);
mMenu = (LinearLayout) View.inflate(this, R.layout.menuview, null);
mSlidingMenu.setMenu(mMenu);
mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);
basically what it does is replacing the linearlayout in decor view with the slidingmenu instead.
Notice: Ive only tested it lightly but it seems to work.
public class ImprovedSlidingPaneLayout extends SlidingPaneLayout {
Context context;
FrameLayout left;
FrameLayout right;
Boolean canOpen = true;
public ImprovedSlidingPaneLayout(Context context) {
super(context);
this.context = context;
this.left = new FrameLayout(context);
this.right = new FrameLayout(context);
this.addView(left);
this.addView(right);
}
public ImprovedSlidingPaneLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (canOpen)
return super.onInterceptTouchEvent(ev);
else
return false;
}
public ImprovedSlidingPaneLayout canOpen(Boolean canOpen) {
this.canOpen = canOpen;
return this;
}
public ImprovedSlidingPaneLayout makeActionBarSlide(Window window){
ViewGroup decorView = (ViewGroup) window.getDecorView();
ViewGroup mainContent = (ViewGroup) decorView.getChildAt(0);
decorView.removeView(mainContent);
setContentView(mainContent);
decorView.addView(this);
return this;
}
public ImprovedSlidingPaneLayout setMenuView(View view){
if((left.getChildCount()== 1)){
left.removeView(left.getChildAt(0));
}
left.addView(view);
return this;
}
public ImprovedSlidingPaneLayout setContentView(View view){
if((right.getChildCount()== 1)){
right.removeView(right.getChildAt(0));
}
right.addView(view);
return this;
}
public ImprovedSlidingPaneLayout setMenuWidth(int width){
left.setLayoutParams(new SlidingPaneLayout.LayoutParams(width, ViewGroup.LayoutParams.MATCH_PARENT));
return this;
}
}
this is my class extends SlidingPaneLayout. Can slide with actio
Thanks for reading!
Some background:
I am building a Gallery app from the tutorial here
Only change I made to this code is to replace
i.setLayoutParams(new Gallery.LayoutParams(150, 100));
with
i.setLayoutParams(new Gallery.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
to display only one gallery image at a time (Kinda like a slideshow viewer).
Problem:
I want the first thumbnail of the gallery to act as an Album Cover with two TextView's to display Album info.
Experiment:
So, I created an cover.xml like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:padding="6dip">
<ImageView android:id="#+cover/imgImage" android:adjustViewBounds="true"
android:layout_width="fill_parent" android:layout_height="fill_parent">
</ImageView>
<TextView android:id="#+cover/tvCoverText1"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:maxLines="2" android:text="Text1" />
<TextView android:id="#+cover/tvCoverText2"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:singleLine="true" android:maxLines="1" android:layout_below="#cover/tvCoverText1"
android:text="Text2" />
</RelativeLayout>
And here's the Java code. I check in getView() if the position is 0 (the first thumbnail) and then play around with the views.
package com.sagar.sample;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class Main extends Activity {
private LayoutInflater mInflater = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Gallery g = (Gallery) findViewById(R.main.gallery);
g.setAdapter(new ImageAdapter(this));
g.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position, long id) {
Toast.makeText(Main.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
}
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private Integer[] mImageIds = {
0, R.drawable.bp1, R.drawable.bp2, R.drawable.bp3
};
public ImageAdapter(Context c) {
mContext = c;
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
// mGalleryItemBackground = a.getResourceId(
// R.styleable.HelloGallery_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) {
ViewHolder viewHolder;
View view = convertView;
if(view == null) {
view = mInflater.inflate(R.layout.cover, null);
viewHolder = new ViewHolder();
viewHolder.tvCoverText1 = (TextView)view.findViewById(R.cover.tvCoverText1);
viewHolder.tvCoverText2 = (TextView)view.findViewById(R.cover.tvCoverText2);
viewHolder.imgView = (ImageView)view.findViewById(R.cover.imgImage);
view.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder)view.getTag();
}
if(position == 0) {
viewHolder.tvCoverText1.setVisibility(View.VISIBLE);
viewHolder.tvCoverText2.setVisibility(View.VISIBLE);
viewHolder.imgView.setVisibility(View.GONE);
}
else {
viewHolder.tvCoverText1.setVisibility(View.GONE);
viewHolder.tvCoverText2.setVisibility(View.GONE);
viewHolder.imgView.setVisibility(View.VISIBLE);
//viewHolder.imgView = new ImageView(mContext);
viewHolder.imgView.setImageResource(mImageIds[position]); //Album cover is at 0th position
viewHolder.imgView.setLayoutParams(new Gallery.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
viewHolder.imgView.setScaleType(ImageView.ScaleType.FIT_XY);
viewHolder.imgView.setBackgroundResource(mGalleryItemBackground);
}
return view;
}
}
static class ViewHolder {
TextView tvCoverText1, tvCoverText2;
ImageView imgView;
}
}
End Result:
When the app loads up, I first see a blank screen for a while and then the view changes to display the AlbumCover. And it's painfully slow to scroll across the images.
Hmm..obviously, I am doing something wrong. I sincerely hope someone could help me here :(
Thanks!
UPDATE: Adding main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<Gallery
android:id="#+main/gallery"
android:layout_width="fill_parent" android:layout_height="fill_parent"
/>
</RelativeLayout>
UPDATE2: So, here's some psuedo code to explain what I am trying to achieve:
if(position == 0)
//show toptext and bottomtext from cover.xml
else
//show tvTitle1 and tvTitle2 (may later include tvTitle3 and tvTitle4) from main.xml
Right now, only the position 0 case works and that too when I swipe to position 1 and swipe back to position 0 - the TextViews are grayed out and barely visible. :(
You need to set the text in your textviews everytime and not only when you inflate the view.
You should probably have 1 single layout file which holds the ui components in the gallery. Right now you have 2 TextView components which are independent of the Gallery. Instead, create some layout resource like this:
gallery_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="true"
android:background="#android:drawable/picture_frame"
android:layout_centerInParent="true" />
<TextView
android:id="#+main/tvTitle1" android:text="Title 1"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_above="#+main/tvTitle2">
</TextView>
<TextView
android:id="#+main/tvTitle2" android:text="Title 2"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentLeft="true">
</TextView>
</RelativeLayout>
So in your getView:
View v = convertView;
if(v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.gallery_item, null);
holder = new ViewHolder();
holder.text1 = (TextView)v.findViewById(R.id.tvTitle1);
holder.text2 = (TextView)v.findViewById(R.id.tvTitle2);
holder.imageView = (ImageView)v.findViewById(R.id.imageView);
v.setTag(holder);
}
else
holder = (ViewHolder)v.getTag();
if(position == 0) {
holder.text1.setVisibility(View.VISIBLE);
holder.text2.setVisibility(View.VISIBLE);
holder.imageView.setVisibility(View.GONE);
}
else {
holder.text1.setVisibility(View.GONE);
holder.text2.setVisibility(View.GONE);
holder.imageView.setVisibility(View.VISIBLE);
}
return v;
You may find that there is some strange behavior going on when you scroll through items, so you might have to directly access each UI component rather than using the holder:
((TextView)v.findViewById(R.id.tvTitle1)).setVisibility(View.GONE);, etc
You also may be interested in setting different types of views for getView: http://developer.android.com/reference/android/widget/Adapter.html#getItemViewType(int)
After trying all approaches suggested here, I wasn't able to still get a custom gallery the way I wanted it to. I kept running into ClassCastException. So, here's what worked for me till now. This is just a workaround and incase someone comes up with a better way to design a custom gallery - do post your answer here so I can accept it.
Thanks for helping out!