Custom Spinners/drop down menu - android

In the app Astrid Tasks, there is a button. When you press the button, a drop down menu comes up.
It's basically a spinner but in a drop-down-list form.
Does anyone know how to do something similar? Is this a widget I just don't see?

As the original author of this (I'm one of the primary Android developers for Astrid) I'd be happy to share how Astrid does it. I'll post the basics here, but you can find more details at our github repo (https://github.com/todoroo/astrid). The basic idea is to extend GreenDroid's QuickActionWidget as hanry suggests. The subclass looks something like:
public class MenuPopover extends QuickActionWidget {
protected DisplayMetrics metrics;
protected LinearLayout content;
public MenuPopover(Context context) {
super(context);
setContentView(R.layout.my_layout);
content = (LinearLayout) getContentView().findViewById(R.id.content);
metrics = context.getResources().getDisplayMetrics();
setFocusable(true);
setTouchable(true);
}
#Override
protected void populateQuickActions(List<QuickAction> quickActions) {
// Do nothing
}
#Override
protected void onMeasureAndLayout(Rect anchorRect, View contentView) {
contentView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentView.measure(MeasureSpec.makeMeasureSpec(getScreenWidth(), MeasureSpec.EXACTLY),
ViewGroup.LayoutParams.WRAP_CONTENT);
int rootHeight = contentView.getMeasuredHeight();
int offsetY = getArrowOffsetY();
int dyTop = anchorRect.top;
int dyBottom = getScreenHeight() - anchorRect.bottom;
boolean onTop = (dyTop > dyBottom);
int popupY = (onTop) ? anchorRect.top - rootHeight + offsetY : anchorRect.bottom - offsetY;
setWidgetSpecs(popupY, onTop);
}
}
The layout file my_layout.xml is pretty simple:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<LinearLayout
android:id="#+id/content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/gdi_arrow_up"
android:orientation="vertical"/>
<ImageView
android:id="#+id/gdi_arrow_up"
android:layout_width="27dip"
android:layout_height="27dip"
android:layout_marginLeft="-10dip"
android:scaleType="fitCenter"
android:layout_marginBottom="-8dip"
android:src="?attr/asListArrowUp" />
<ImageView
android:id="#+id/gdi_arrow_down"
android:layout_width="27dip"
android:layout_height="27dip"
android:scaleType="fitCenter"
android:layout_marginBottom="-8dip"
android:layout_below="#android:id/list"/>
</RelativeLayout>
</FrameLayout>
Then, you can just add a simple helper method to the popover class to add views (i.e. rows, with optional listeners) to the main body of the popover:
public void addViewToContent(View v, OnClickListener listener) {
content.addView(v);
if (listener != null) {
v.setOnClickListener(listener);
}
}
After creating an instance of the popup, you can show it by calling
menuPopover.show(anchorView);
This is a somewhat simplified version -- in practice, we attach some addition information, listeners, etc. to those views to make them actually do things when clicked. If you want, you can check out the full code at https://github.com/todoroo/astrid -- the class is com.todoroo.astrid.ui.MainMenuPopover.
Thanks for using Astrid!

Related

Fixed size custom View's UI components is not visible on screen

I have a fixed size custom view as follow.
RateAppBanner.java
public class RateAppBanner extends LinearLayout {
public RateAppBanner(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.rate_app_banner, this, true);
this.setBackgroundColor(Color.RED);
View view = this.findViewById(R.id.textView1);
if (view == null) {
Log.i("CHEOK", "WTF!");
} else {
// Reach here!
Log.i("CHEOK", "---> " + ((TextView)view).getText());
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = 320;
int h = 100;
setMeasuredDimension(w, h);
}
}
rate_app_banner.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ffff0000"
android:text="Hello World" />
</merge>
However, I realize the textView1 is not visible on screen, even thought I can find it through findViewById.
I use the dump View Hierarchy through Eclipse. Here's what I get.
This puzzles me a lot. I can't see textView1 in the view hierarchy, even I can discover it through findViewById. Do you have any idea why textView1 is not visible on screen?
This is how I add RateAppBanner into my main activity.
MainActivity.java
import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout mainView = (LinearLayout)this.findViewById(R.id.screen_main);
mainView.addView(new RateAppBanner(this));
}
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/screen_main">
</LinearLayout>
</FrameLayout>
Complete source code
The complete workable souce code, which compiled using Eclipse can be downloaded from abc.zip
p/s The code example shown in rate_app_banner.xml is a stripped down version of complex UI layout. To narrow down the problem scope, I make the layout file contain only single TextView.
Another approach
Instead of using merge, I had tried
rate_app_banner.xml
<?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">
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ffff0000"
android:text="Hello World" />
</LinearLayout>
It doesn't work still...
I tested using your source abc.zip. Please change the red color of text view to other. then add
onMeasure(w, h);
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = 320;
int h = 200;
super.onMeasure(w, h);
setMeasuredDimension(w, h);
}

wrap_content height, but limited to half of parent

What I want is to have a layout with 2 views arranged vertically. Let's call the top view A and the bottom one B. I want the amount of height given to B to be its normal height (i.e. wrap content) except that I don't want it to be given more than half of the available space. A gets what is left.
Another way to word it is that A should always get at least 50% of the available height and B should get at most 50%.
I can't seem to find an easy way to achieve that. I can set both layout heights to 0 and give them equal weights which makes them both 50% always, but if B is smaller than 50% it should be given only what it needs.
The only way I can see to do it is use a custom class for A or B and override onMeasure to constrain the height to 50% of the parent, but it seems there should be an easier way.
Ok, I got it now. If I understood correctly you want to have it like this:
if A > B -> do nothing
if B > A & B > parent layout -> 50% to both of them
if B > A & B < parent layout -> A = parent layout - B
I had to do it all in onWindowFocusChanged because otherwise in onCreate the height of the Views would return 0. I did it with 2 LinearLayouts as child layouts, but you can take what ever you want.
My XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/parent_lay"
android:orientation="vertical"
android:layout_height="match_parent" >
//Layout A:
<LinearLayout
android:id="#+id/lay_1"
android:layout_width="match_parent"
android:background="#android:color/background_dark"
android:layout_height="10dp" >
</LinearLayout>
//Layout B:
<LinearLayout
android:id="#+id/lay_2"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#123456" >
</LinearLayout>
</LinearLayout>
MainActivity:
public class MainActivity extends Activity {
LinearLayout parent_lay;
LinearLayout lay_1;
LinearLayout lay_2;
int parent_height;
int lay_1_height;
int lay_2_heigth;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
parent_lay = (LinearLayout) findViewById(R.id.parent_lay);
lay_1 = (LinearLayout) findViewById(R.id.lay_1);
lay_2 = (LinearLayout) findViewById(R.id.lay_2);
lay_1_height = lay_1.getHeight();
lay_2_heigth = lay_2.getHeight();
parent_height = parent_lay.getHeight();
if (lay_2.getHeight() > lay_1.getHeight()
&& lay_2.getHeight() > (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
lay_2.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, 0, 1));
} else if (lay_2.getHeight() < (parent_lay.getHeight() / 2)) {
lay_1.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, (parent_height - lay_2_heigth)));
}
}
}
Example:
If A is 60dp and B is 40dp:
If A is 60dp and B is 400dp:
You must write your own component to achieve this.
For example, if you use LinearLayout here, you can extends a LinearLayout with overdid onMeasure method. You can implement onMeasure like this:
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
setMeasuredDimension(width, height / 2);
}
This code is not elegant enough. If you really want to do it well, copy the original onMeasure method from Android source code (http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/widget/LinearLayout.java#LinearLayout.onMeasure%28int%2Cint%29), and in measureVertical(int widthMeasureSpec, int heightMeasureSpec), set mTotalLength = mTotalLength / 2.
For detailed information of onMeasure, visit http://developer.android.com/guide/topics/ui/custom-components.html and http://developer.android.com/reference/android/view/View.html#onMeasure(int, int).
Now the desired effect can be achieved with the ConstraintLayout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/containerFrameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
<FrameLayout
android:id="#+id/containerFrameLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guideline"
app:layout_constraintVertical_bias="1">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
Create a linear layout with two inner frames, each with .5 weight. Inside those frames, place your views, setting them to wrap_content or match_parent as appropriate.

Can ViewPager have multiple views in per page?

After trying out the Gallery and Horizontal Scroll View, I found that the View Pager does what I need but with one minor thing missing. Can the View Pager have multiple views per page?
I know that View Pager shows only 1 view/page per swipe. I was wondering if I can limit my views width so my 2nd view following it will show?
For example: I have 3 views and I want the screen to show view 1 and part of view 2 so the user knows there is more content so they can swipe to view 2.
|view 1|view 2|view 3|
|screen |
I discovered that a perhaps even simpler solution through specifying a negative margin for the ViewPager. I've created the MultiViewPager project on GitHub, which you may want to take a look at:
https://github.com/Pixplicity/MultiViewPager
Although MultiViewPager expects a child view for specifying the dimension, the principle revolves around setting the page margin:
ViewPager.setPageMargin(
getResources().getDimensionPixelOffset(R.dimen.viewpager_margin));
I then specified this dimension in my dimens.xml:
<dimen name="viewpager_margin">-64dp</dimen>
To compensate for overlapping pages, each page's content view has the opposite margin:
android:layout_marginLeft="#dimen/viewpager_margin_fix"
android:layout_marginRight="#dimen/viewpager_margin_fix"
Again in dimens.xml:
<dimen name="viewpager_margin_fix">32dp</dimen>
(Note that the viewpager_margin_fix dimension is half that of the absolute viewpager_margin dimension.)
We implemented this in the Dutch newspaper app De Telegraaf Krant:
Mark Murphy has an interesting blog post addressing precisely this problem. Although I ended up using my own solution in this thread, it's worthwhile looking at Dave Smith's code, which Mark references in the blog post:
https://gist.github.com/8cbe094bb7a783e37ad1/
Warning! Before you take this approach, beware of some very serious issues with this approach, mentioned both at the end of this post and in the comments below.
You'll end up with this:
It effectively works by wrapping a ViewPager into a subclass of FrameLayout, setting it to a specific size, and calling setClipChildren(false). This inhibits Android from clipping the views that exceed beyond the boundaries of the ViewPager, and visually accomplishes what you want.
In XML, it's very simple:
<com.example.pagercontainer.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#CCC">
<android.support.v4.view.ViewPager
android:layout_width="150dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
</com.example.pagercontainer.PagerContainer>
Add in a little code for handling touch events from outside of the ViewPager and invalidating the display when scrolling, and you're done.
That being said, and while this works great in general, I did notice that there is an edge-case that isn't solved with this fairly simple construction: when calling setCurrentPage() on the ViewPager. The only way I could find to resolve this was by subclassing ViewPager itself and having its invalidate() function also invalidate the PagerContainer.
It is possible to show more than one page on the same screen.
One of the ways is by overriding the getPageWidth() method in the PAgerAdapter. getPageWidth() returns a float number between 0 and 1 indicating how much width of the Viewpager should the page occupy. By default it is set to 1. So, you can change this to the width you wish.
You can read more about this here & github project.
This is how I got it:
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:gravity="center"
android:paddingLeft="36dp"
android:paddingRight="36dp"/>
and in activity,i use this :
markPager.setPageMargin(64);
hope it helps!
I had the same problem with the only difference that i needed to show 3 pages at once (previous, current and next pages). After a really long research for the best solution i think i found it.
The solution is a mix of few of the answers here:
As #Paul Lammertsma's answer pointed out - Dave Smith's code in Mark Murphy's blog is the basis for the solution. The only problem for me was that the ViewPager was only on the top part of the screen due to the size they give it in the xml file:
android:layout_width="150dp"
android:layout_height="100dp"
Which wasn't good for my purpose since i was looking for something that will spread all over the screen. So i changed it to wrap the content as you can see here:
<com.example.nutrino_assignment.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCC">
<android.support.v4.view.ViewPager
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</com.example.nutrino_assignment.PagerContainer>
Now I lost all the effect of what the tutorial was trying to do. Using #andro's answer i was able to show more then 1 page at a time: exactly 2! The current and the next.
Did so by overriding as follow:
#Override
public float getPageWidth(int position) {
return(0.9f);
}
That was almost what i needed... (even though i think its enough for what you were asking), but for others who might need something like what i was needed:
For the last part of the solution i used the idea in this answer, again by #Paul Lammertsma.
In Dave Smith's code you will find in the onCreate method this line:
//A little space between pages
pager.setPageMargin(15);
which i replaced with:
//A little space between pages
pager.setPageMargin(-64);
now on the first page looks:
|view 1|view 2|view 3|
|screen |
while on the 2nd it looks like:
|view 1|view 2|view 3|
|screen |
Hope it will help someone! I wasted like 2 days on it...
Good luck.
viewPager.setPageMargin(-18);// adjust accordingly ,-means less gap
in imageadapter
private class ImagePagerAdapter2 extends PagerAdapter {
private int[] mImages = new int[] {
R.drawable.add1,
R.drawable.add3,
R.drawable.add4,
R.drawable.add2,
};
#Override
public float getPageWidth(int position) {
return .3f;
}
adjust return value...lesser means more image......0.3 means atleast 3 images at a time.
LayoutParams lp = new LayoutParams(width,height);
viewpager.setLayoutParams(lp);
In xml file using this code(Main Activity)
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="130dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical"
android:weightSum="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="130dp">
<com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="100dip"
android:layout_height="100dip"/>
</com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer>
</RelativeLayout>
</LinearLayout>
Main activity xml file add this code
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="130dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical"
android:weightSum="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="130dp">
<com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="100dip"
android:layout_height="100dip"/>
</com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer>
</RelativeLayout>
</LinearLayout>
Main Activity code
public class MainActivity extends Activity{
final Integer[] XMEN2= {R.mipmap.bookticket,R.mipmap.safty,R.mipmap.privacy};
private ArrayList<Integer> XMENArray2 = new ArrayList<Integer>();
PagerContainer mContainer;
int currentPage2 = 0;
private static int NUM_PAGES2 = 0;
ViewPager mPager2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData2();}
private void initViews() {
mPager2 = (ViewPager)findViewById(R.id.viewpager);
mContainer = (PagerContainer)findViewById(R.id.pager_container);
mPager2.setOffscreenPageLimit(5);
mPager2.setPageMargin(15);
mPager2.setClipChildren(false);
}
private void initData2() {
for(int i=0;i<XMEN2.length;i++)
XMENArray2.add(XMEN2[i]);
mPager2.setAdapter(new Sliding_Adaptertwo(getActivity(),XMENArray2));
NUM_PAGES2 =XMEN2.length;
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage2 == NUM_PAGES2) {
currentPage2= 0;
}mPager2.setCurrentItem(currentPage2++, true);
}
};
Timer swipeTimer = new Timer();
swipeTimer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(Update);
}
}, 3000, 3000);
}
}
Pager View pagercontainer class
import android.content.Context;
import android.graphics.Point;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class PagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener {
private ViewPager mPager;
boolean mNeedsRedraw = false;
public PagerContainer(Context context) {
super(context);
init();
}
public PagerContainer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PagerContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
//Disable clipping of children so non-selected pages are visible
setClipChildren(false);
//Child clipping doesn't work with hardware acceleration in Android 3.x/4.x
//You need to set this value here if using hardware acceleration in an
// application targeted at these releases.
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
try {
mPager = (ViewPager) getChildAt(0);
mPager.setOnPageChangeListener(this);
} catch (Exception e) {
throw new IllegalStateException("The root child of PagerContainer must be a ViewPager");
}
}
public ViewPager getViewPager() {
return mPager;
}
private Point mCenter = new Point();
private Point mInitialTouch = new Point();
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenter.x = w / 2;
mCenter.y = h / 2;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
//We capture any touches not already handled by the ViewPager
// to implement scrolling from a touch outside the pager bounds.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialTouch.x = (int)ev.getX();
mInitialTouch.y = (int)ev.getY();
default:
ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
break;
}
return mPager.dispatchTouchEvent(ev);
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Force the container to redraw on scrolling.
//Without this the outer pages render initially and then stay static
if (mNeedsRedraw) invalidate();
}
#Override
public void onPageSelected(int position) { }
#Override
public void onPageScrollStateChanged(int state) {
mNeedsRedraw = (state != ViewPager.SCROLL_STATE_IDLE);
}
}
and its Adapter
public class Sliding_Adaptertwo extends PagerAdapter {
private ArrayList<Integer> IMAGES;
private LayoutInflater inflater;
private Context context;
public Sliding_Adaptertwo(Context context, ArrayList<Integer> IMAGES) {
this.context = context;
this.IMAGES=IMAGES;
inflater = LayoutInflater.from(context);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return IMAGES.size();
}
#Override
public Object instantiateItem(ViewGroup view, int position) {
View imageLayout = inflater.inflate(R.layout.sliding_layout, view, false);
assert imageLayout != null;
final ImageView imageView = (ImageView) imageLayout
.findViewById(R.id.image);
imageView.setImageResource(IMAGES.get(position));
view.addView(imageLayout, 0);
return imageLayout;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
#Override
public Parcelable saveState() {
return null;
}
}
xml file of adapter class
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="#+id/image"
android:layout_width="90dp"
android:layout_height="90dp"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="#drawable/ad1"
/>
</FrameLayout>
it works fine

getWidth and getHeight are returning a zero

I am trying to make a simple drawing program for the android.
I have a custom View class to handle the drawing. When I call its getWidth and getHeight metheds, I get a zero.
But, the drawing works fine, I hard code in the width and height so it works. Why is it doing this?
My View class
public class cDrawing extends View{
char BitMap[];
static final short WIDTH=160;
static final short HEIGHT=440;
static final char EMPTY=' ';
int mWidthSize;
int mHeightSize;
static final char RED ='R';
int y;
public cDrawing(Context context) {
super(context);
y=3;
// set up our bitmap
BitMap=new char[WIDTH*HEIGHT];
for(int i=0; i<WIDTH*HEIGHT; i++)
BitMap[i]=EMPTY;
// returns zero why???????
int h=getHeight();
h=400;
int w=getWidth();
w=320;
mWidthSize=w/WIDTH;
mHeightSize=h/HEIGHT;
// TODO Auto-generated constructor stub
}
The Activity class
public class cCanves extends Activity implements OnClickListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.canves);
cDrawing board=new cDrawing(this);
LinearLayout layout = (LinearLayout)findViewById(R.id.parent2);
layout.addView(board);
// set up buttons
View mEraser = findViewById(R.id.buteraser);
mEraser.setOnClickListener(this);
View mBlack = findViewById(R.id.butblack);
mBlack.setOnClickListener(this);
View mWhite = findViewById(R.id.butwhite);
mWhite.setOnClickListener(this);
View mRed = findViewById(R.id.butred);
mRed.setOnClickListener(this);
} // end function
public void onClick(View v) {
Intent i;
switch(v.getId())
{
case R.id.buteraser:
break;
case R.id.butblack:
break;
case R.id.butwhite:
break;
case R.id.butred:
break;
} // end switch
} // function
}
the xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="#+id/parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="fill_parent">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_height="wrap_content">
<ImageButton android:id="#+id/buteraser"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton android:id="#+id/butblack"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton android:id="#+id/butwhite"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton android:id="#+id/butred"
android:src="#drawable/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:id="#+id/parent2"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="fill_parent">
</LinearLayout>
</LinearLayout>
The width and height are not defined until the view is actually rendered to the screen.
Use protected abstract void onLayout (boolean changed, int l, int t, int r, int b) (which you override in your activity) to know when the sizes are ready.
Complementing Mah's answer, I found out that you can get the values from parameters, like the code bellow:
ImageView imageProcess = (ImageView) li.inflate(
R.layout.andamento_sinistro_imageprocess, centerLayout, false);
imageProcess.setBackgroundResource(
(isActive)?(R.drawable.shape_processon):(R.drawable.shape_processoff));
RelativeLayout.LayoutParams imageProcessParams =
(RelativeLayout.LayoutParams)imageProcess.getLayoutParams();
imageProcessParams.leftMargin =
(int) (centerPosition - 0.5*imageProcessParams.width);
imageProcessParams.addRule(RelativeLayout.CENTER_VERTICAL);
centerLayout.addView(imageProcess);
The real catch here is the use of the LayoutParams, that have rules yet not processed by the element.
I'm not certain, but it may have something to do with where the code is in the lifecycle of your activity. If you're calling getWidth() and getHeight() before the View is displayed on screen, you'll get a value of 0. I've had that happen to me, too.
I'm not sure if there's a way around this. I had to rely on getting the hardware screen's width and height, instead of the view's width and height. You might end up having to approximate the width and height of your view and hard coding it.
You should call getWidth() and getHeight() in the overrided method onLayout.
Just Use the getViewTreeObserver() Listener, and inside this just
calculate the height and width.
Follow the code :
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//Do your Stuff calculation , like view.getWidth() ...
});
The view only has dimensions after beeing displayed for the first time.
Other thing:
int h=getHeight();
h=400;
Is useless no ? Is it just for testing ?

How to use the xml setting in a view of a activity?

I want to show two views in one activity. If I clicked on button in the first view I want to see the second and other way round.
The views should not have the same size as the screen so I want e.g. to center it, like you see in first.xml.
But if I add the views with
addContentView(mFirstView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
the views are not centered. They are shown at top left.
How can I use the xml settings to e.g. center it?
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:background="#drawable/background"
android:layout_gravity="center"
android:minWidth="100dp"
android:minHeight="100dp"
android:paddingBottom="5dp"
>
<LinearLayout android:id="#+id/head"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton android:id="#+id/first_button"
android:src="#drawable/show_second"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#null" />
</LinearLayout>
second.xml same as first.xml but with
<ImageButton android:id="#+id/second_button"
android:src="#drawable/show_first"
... />
ShowMe.java
public class ShowMe extends Activity {
View mFirstView = null;
View mSecondView = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initFirstLayout();
initSecondLayout();
showFirst();
}
private void initFirstLayout() {
LayoutInflater inflater = getLayoutInflater();
mFirstView = inflater.inflate(R.layout.first, null);
getWindow().addContentView(mFirstView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
ImageButton firstButton = (ImageButton)mMaxiView.findViewById(R.id.first_button);
firstButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ShowMe.this.showSecond();
}
});
}
private void initSecondLayout() {
// like initMaxiLayout()
}
private void showFirst() {
mSecondView.setVisibility(View.INVISIBLE);
mFirstView.setVisibility(View.VISIBLE);
}
private void showSecond() {
mFirstView.setVisibility(View.INVISIBLE);
mSecondView.setVisibility(View.VISIBLE);
}}
Hope someone can help.
Thanks
Why don't you use setContentView(R.layout.yourlayout)? I believe the new LayoutParams you're passing in addContentView() are overriding those you defined in xml.
Moreover, ViewGroup.LayoutParams lacks the layout gravity setting, so you would have to use the right one for the layout you're going to add the view to (I suspect it's a FrameLayout, you can check with Hierarchy Viewer). This is also a general rule to follow. When using methods that take layout resources as arguments this is automatic (they might ask for the intended parent).
With this consideration in mind, you could set your layout params with:
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(/* wrap wrap */);
lp.setGravity(Gravity.CENTER);
addContentView(mYourView, lp);
But I would recommend setContentView() if you have no particular needs.
EDIT
I mean that you create a layout like:
~~~/res/layout/main.xml~~~
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="....."
android:id="#+id/mainLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
then in your onCreate() or init...Layout():
setContentView(R.layout.main);
FrameLayout mainLayout = (FrameLayout)findViewById(R.id.mainLayout);
// this version of inflate() will automatically attach the view to the
// specified viewgroup.
mFirstView = inflater.inflate(R.layout.first, mainLayout, true);
this will keep the layout params from xml, because it knows what kind it needs. See reference.

Categories

Resources