Android View getWidth method always returns 0 - java

I have a GridView to put some images in it. What I would like to do is to have the measurements of the GridView such as width and height so that I know what should be the optimal size of images when they are being showed in the getView() method. I want to show only 8 image per row. So say if a device has bigger screen the images will have bigger size instead of adding more image in the row by setting a fixed size for images.
So in the onCreate() method I initialize my custom Adapter and pass the getWidth and getHeight values into it. But they are always zero.
In the xml layout file, gridview was the only view, then I added it to a linearlayout so maybe it atleast return the width and height of its parent...but that is still zero.
Here is the onCreate method of the Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
if (savedInstanceState == null) {
}
int difficulty = getIntent().getExtras()
.getInt(AppConstants.EXTRAS_GAME_DIFFICULTY_LEVEL, 1);
LinearLayout lvg = (LinearLayout) findViewById(R.id.linearForGridGame);
GridView gv = (GridView) findViewById(R.id.gridview);
gv.setAdapter(new CellAdapter(this, difficulty, lvg.getWidth(), lvg.getHeight()));
gv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(GameActivity.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
}
Here is the xml layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearForGridGame">
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="128px"
android:numColumns="auto_fit"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:stretchMode="none"
android:gravity="center"
android:choiceMode="none"
android:listSelector="#null"
android:clickable="true" />
</LinearLayout>
And here is the cosntructor of the adapter, where I get width and height always 0:
public CellAdapter(Context context, int difficultyLevel, int parentWidth, int parentHeight)
{
_context = context;
_dificultyLevel = difficultyLevel;
_parentHight = parentHeight;
_parentWidth = parentWidth;
Log.d(TAG, "Received difficulty level " + _dificultyLevel); //OK
Log.d(TAG, "Received parent width " + _parentWidth); //Always 0
Log.d(TAG, "Received parent height " + _parentHight); //Always 0
_cellWidth = (_parentWidth / 6); //Width of image to fill 6 per row
setupGame(_dificultyLevel);
}

You must wait until the view hierarchy is inflated and measured to know the dimensions. Add something like that in onCreate()
final ViewTreeObserver vto = lvg.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (lvg.getWidth() > 10){ // because it may be called before the view is measured and you will still get 0
// here you can get the measured dimensions
ViewTreeObserver obs = pictureImg.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this); // otherwise you're gonne keep getting called
}
}
});

Try doing lvg.getWidth() in onWindowFocusChanged(boolean ) of the activity

I think you have to use LayoutParams, like this:
...
LinearLayout lvg = (LinearLayout) findViewById(R.id.linearForGridGame);
ViewGroup.LayoutParams layoutParams = lvg.getLayoutParams();
GridView gv = (GridView) findViewById(R.id.gridview);
gv.setAdapter(new CellAdapter(this, difficulty, layoutParams.getWidth(), layoutParams.getHeight()));
...
Be sure to import android.view.ViewGroup.LayoutParams .

View layout hasn't occurred at onCreate(), you can see for yourself by subclassing View and observing when onLayout() is called.
Lots of info in this question. A ViewTreeObserver.OnGlobalLayoutListener may be more specific to your desire than onWindowFocusChanged(boolean), as you can target a particular view.
Example from this answer:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Ensure you call it only once :
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// Here you can get the size :)
}
});

Related

Android GridView not showing images

I have made a GridView with an ImageAdapter but it does not show the images inside.
I tried to change numColumns, columnWidth and other attributes but it didn't work.
In Android Studio xml Design panel i can see my Gridview.
This is my gridview inside my xml layout file:
<GridView
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/linear_buttons"
android:layout_above="#+id/btnSearch"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:background="#null"
android:columnWidth="120dp"
android:gravity="center"
android:numColumns="auto_fit"
android:stretchMode="columnWidth" />
This is my Adapter:
public class CustomGridViewAdapter extends BaseAdapter {
private final Context mContext;
public CustomGridViewAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(R.dimen.grid_dimens_width, R.dimen.grid_dimens_height));
imageView.setScaleType(ImageView.ScaleType.CENTER);
imageView.setPadding(1, 5, 1, 1);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
private final Integer[] mThumbIds = {
R.drawable.grid_agapis, R.drawable.grid_asteies, R.drawable.grid_auto,
R.drawable.grid_gamos, R.drawable.grid_goneis,
};
I set the adapter with the following code:
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new CustomGridViewAdapter(this));
gridview.setOnItemClickListener(this);
Can you explain me where is the problem?
Thank you.
imageView.setLayoutParams(new GridView.LayoutParams(R.dimen.grid_dimens_width,
R.dimen.grid_dimens_height));
With this line of code you are trying to limit the size of the grabbed drawable to a fixed width & height that are equal to grid_dimens_width & grid_dimens_height respectively.
But actually using R.dimen.foo won't return the value of foo, instead it returns the generated integer value of the resource itself which can be something like a big number (e.g. -21893103 or 33238590) .. this will make you see nothing on the screen because the image is either:
Too big (in case of a positive resource value 33238590) so you are seeing the tiny pixels of it
or too small (in case of a negative resource value -21893103) because its size is zero.
What you need to do instead is to get the dimen resource using getDimention() and pass the resource id to it.
To apply that to your code:
Replace:
imageView.setLayoutParams(new GridView.LayoutParams(R.dimen.grid_dimens_width,
R.dimen.grid_dimens_height));
With:
imageView.setLayoutParams(new GridView.LayoutParams(
(int) mContext.getResources().getDimension(R.dimen.grid_dimens_width),
(int) mContext.getResources().getDimension(R.dimen.grid_dimens_height)));
Result:

Checking dimensions of View in MainActivity

I want to get dimensions of View defined in activity_main.xml to create a Bitmap based on height and width. Writing them in textView shows 0 or stops app. I checked if this view is null, but it's not. Can I get dimensions of view this way?
Here is the code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.textView);
View view = (View) findViewById(R.id.view);
int x = view.getMeasuredWidth();
int y = view.getHeight();
int radius = 10;
textView.setText(view.getHeight());
// if (view == null) textView.setText("View is null");
// else textView.setText("View is not null");
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Here is actvity_main.xml:
<View
android:id="#+id/view"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_marginLeft="44dp"
android:layout_marginStart="44dp"
android:layout_marginTop="136dp"
android:background="#color/green"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/textView"
android:layout_width="209dp"
android:layout_height="48dp"
android:layout_marginLeft="44dp"
android:layout_marginStart="44dp"
android:layout_marginTop="28dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Your are trying to set integer value in TextView in this line textView.setText(view.getHeight()); Try Convert it in String an then set as follows.
textView.setText(String.valueOf(view.getHeight()));
for getting view either use above line in onResume or use as follows
textView.post(new Runnable()
{
public void run()
{
textView.setText(String.valueOf(view.getHeight()));
}
});
I hope it's work for you
Thank You
getWidth and height will return 0 as long as the view has not been layedout.
in order to get the dimensions at the right moment you have 2 options:
add a ViewTreeObserver in onCreate and remove it when it gets called.
2.(i think is easier) add a runnable to the view as i have showed at the bottom:
view.post(new Runnable() {
#Override
public void run() {
int x = view.getMeasuredWidth();
int y = view.getHeight();
}
})
the post() method callback gets called only after the view inflating is done.
happy coding
You can try this solution
int height;
int width;
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override public void onGlobalLayout () {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
height = view.getLayoutParams().height;
width = view.getLayoutParams().width;
}
});
Just use this code :)
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
Now you can use height and width variable in your project

Updating TextView within Fragment for Consistency

I am trying to build a quiz app that keeps score as the user answers questions. I'm also using Google's ViewPager for screen slides which employs Fragments to keep different individual pages (code: http://developer.android.com/training/animation/screen-slide.html). I have a TextView (#id/txtvw_score) that is designed to keep score across the entire quiz session. This txtvw_score is placed within fragment_screen_slide_page.xml.
The problem I'm having is that, in order to access said TextView, I have to do it in ScreenSlidePageFragment's onCreateView() method. But, because Android calls this onCreateView() method twice for every new Fragment, it makes it so that the TextView's score is completely outdated. For example, page 1 would display the current score, then page 2 would display page 0 (uninitialized score), then page 3 would display page 1's score, etc.
I'm using SharedPreferences to keep the actual score updated. I've been able to keep track of the score perfectly fine when I use the ActionBar. This is only for confirmation purposes though, as I don't actually want to use the ActionBar.
Further complicating the problem is that I can't update the TextView within the Fragment's onCreate() method. I've also tried calling the TextView and changing it from ScreenSlideActivity (source: How to change fragment's textView's text from activity), but that ended up as NullPointerException no matter what I did.
So, long story short, how can I keep a TextView's content consistent across all Fragments? (This is a significant point for me, because, in the future, I want to use a TextView as a display for a timer as well, and that definitely needs to stay consistent).
EDIT (added simplified code):
fragment_screen_slide_page.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ScrollView
android:id="#+id/scrlvw_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp" >
<TextView
android:id="#+id/txtvw_score"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:textStyle="bold" />
</RelativeLayout>
</LinearLayout>
</ScrollView>
</RelativeLayout>
ScreenSlidePageFragment.java
public class ScreenSlidePageFragment extends Fragment
{
//The argument key for the page number this fragment represents.
public static final String ARG_PAGE = "page";
ArrayList<String> argAnswerList = new ArrayList<String>();
//The fragment's page number, which is set to the argument value for {#link #ARG_PAGE}.
private int mPageNumber;
//preferences
SharedPreferences sharedPrefs;
SharedPreferences preferences;
SharedPreferences.Editor editor;
final int DEFAULT = 0;
final int CHOSEN1 = 1;
final int CHOSEN2 = 2;
final int CHOSEN3 = 3;
final int CHOSEN4 = 4;
TextView txtvwScore;
int questionsTotal;
int numChosenCorrect, numChosenTotal, ratio;
//Factory method for this fragment class. Constructs a new fragment for the given page number.
public static ScreenSlidePageFragment create(int pageNumber)
{
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
fragment.setArguments(args);
return fragment;
}
public ScreenSlidePageFragment()
{
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getActivity().getActionBar().show();
fillArgAnswerList();
//fillAnswerList();
mPageNumber = getArguments().getInt(ARG_PAGE);
getPrefs();
preferences = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
editor = preferences.edit();
numChosenCorrect = sharedPrefs.getInt("numChosenCorrect", DEFAULT);
numChosenTotal = sharedPrefs.getInt("numChosenTotal", DEFAULT);
ratio = sharedPrefs.getInt("ratio", DEFAULT);
getActivity().getActionBar().setTitle("Score: " + numChosenCorrect + "/" + numChosenTotal + " (" + ratio + "%)");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//Inflate the layout containing a title and body text.
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
//setting the score
txtvwScore = (TextView) rootView.findViewById(R.id.txtvw_score);
setButtonOnClickListener();
return rootView;
}
private void setButtonOnClickListener()
{
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
numChosenCorrect = sharedPrefs.getInt("numChosenCorrect", DEFAULT);
numChosenTotal = sharedPrefs.getInt("numChosenTotal", DEFAULT);
ratio = sharedPrefs.getInt("ratio", DEFAULT);
String answer = button.getText().toString();
if(answer.equals(correct))
{
numChosenCorrect++;
numChosenTotal++;
}
else
numChosenTotal++;
//update the actionBar score
ratio = Math.round(((float) numChosenCorrect)/((float) numChosenTotal)*100);
//setting the score
getActivity().getActionBar().setTitle("Score: " + numChosenCorrect + "/" + numChosenTotal + " (" + ratio + "%)");
editor.putInt("numChosenCorrect", numChosenCorrect);
editor.putInt("numChosenTotal", numChosenTotal);
editor.putInt("ratio", ratio);
editor.commit();
}
});
}
/**
* Get the preferences saved.
*/
private void getPrefs()
{
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
}
/**
* Returns the page number represented by this fragment object.
*/
public int getPageNumber()
{
return mPageNumber;
}
}
All codes have been simplified to show what the question revolves around. All removed code has been tested and they work up until this point.

ListView row buttons: How do I create a custom Adapter that connects a View.OnClickListener to a button on each row of a ListView?

I want my ListView to contain buttons, but setting the button's xml property, onClick="myFunction" and then placing a public void myFunction(android.view.View view) method in the activity causes an NoSuchMethodException (the stack trace is null) to be thrown, as although the onclick listener is there, it doesn't fire myFunction(...) and cause the activity to close.
How do I create a custom Adapter that connects a View.OnClickListener to a button on each row of a ListView?
My ListView is created as follows...
[activity.java content..]
public void myFunction(android.view.View view)
{
//Do stuff
}
[activity.xml content..]
<LinearLayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".FrmCustomerDetails" >
<ListView android:id="#+id/LstCustomerDetailsList" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:clickable="true" android:clipChildren="true" android:divider="#null" android:dividerHeight="0dp" android:fastScrollEnabled="true" android:footerDividersEnabled="false" android:headerDividersEnabled="false" android:requiresFadingEdge="vertical" android:smoothScrollbar="true" />
</LinearLayout>
[activity_row_item.xml content..]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="#+id/Llt" android:layout_width="match_parent" android:layout_height="match_parent" >
<Button android:id="#+id/Btn" android:text="Click me" android:onClick="myFunction" />
</LinearLayout>
Here is how to create the custom Adapter, connecting View.OnClickListener to a ListView with a button per row...
1. Create a layout for a typical row
In this case, the row is composed of three view components:
name (EditText)
value (EditText:inputType="numberDecimal")
delete (Button)
Xml
pay_list_item.xml layout is as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<EditText
android:id="#+id/pay_name"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="2"
android:hint="Name" />
<EditText
android:id="#+id/pay_value"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:inputType="numberDecimal"
android:text="0.0" />
<Button
android:id="#+id/pay_removePay"
android:layout_width="100dp"
android:layout_height="fill_parent"
android:text="Remove Pay"
android:onClick="removePayOnClickHandler" />
</LinearLayout>
Note: the button has onClick handler defined in xml layout file, because we want to refer its action to a specific list item.
Doing this means that the handler will be implemented in Activity file and each button will know which list item it belongs to.
2. Create list item adapter
This is the java class that is the controller for pay_list_item.xml.
It keeps references for all of its views, and it also puts these references in tags, extending the ArrayAdapter interface.
The Adapter:
public class PayListAdapter extends ArrayAdapter<Payment> {
private List<Payment> items;
private int layoutResourceId;
private Context context;
public PayListAdapter(Context context, int layoutResourceId, List<Payment> items) {
super(context, layoutResourceId, items);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PaymentHolder holder = null;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new PaymentHolder();
holder.Payment = items.get(position);
holder.removePaymentButton = (ImageButton)row.findViewById(R.id.pay_removePay);
holder.removePaymentButton.setTag(holder.Payment);
holder.name = (TextView)row.findViewById(R.id.pay_name);
holder.value = (TextView)row.findViewById(R.id.pay_value);
row.setTag(holder);
setupItem(holder);
return row;
}
private void setupItem(PaymentHolder holder) {
holder.name.setText(holder.Payment.getName());
holder.value.setText(String.valueOf(holder.Payment.getValue()));
}
public static class PaymentHolder {
Payment Payment;
TextView name;
TextView value;
ImageButton removePaymentButton;
}
}
Here we list the Payment class items.
There are three most important elements here:
PayListAdapter constructor: sets some private fields and calls superclass constructor. It also gets the List of Payment objects. Its implementation is obligatory.
PaymentHolder: static class that holds references to all views that I have to set in this list item. I also keep the Payment object that references to this particular item in list. I set it as tag for ImageButton, that will help me to find the Payment item on list, that user wanted to remove
Overriden getView method: called by superclass. Its goal is to return the single List row. We create its fields and setup their values and store them in static holder. Holder then is put in row’s tag element. Note that there is a performance issue, as the row is being recreated each time it is displayed. I used to add some flag in holder like isCreated, and set it to true after row was already created. then you can add if statement and read tag’s holder instead of creating it from scratch.
Payment.java is quite simple as for now and it looks a bit like BasicNameValuePair:
public class Payment implements Serializable {
private String name = "";
private double value = 0;
public Payment(String name, double value) {
this.setName(name);
this.setValue(value);
}
...
}
There are additional gets and sets for each private field not shown.
3. Add ListView to the activity layout xml file
In its simpliest form, it will be enough to add this view to activity layout:
<ListView
android:id="#+id/EnterPays_PaysList"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
4. Set up adapter to this list view in Activity Java code
In order to display items in ListView you need to set up its adapter and map it to some other ArrayList of Payment objects (as I am extending an Array adapter here). Here is code that is responsible for binding adapter to editPersonData.getPayments() ArrayList:
PayListAdapter adapter = new PayListAdapter(AddNewPerson.this, R.layout.pay_list_item, editPersonData.getPayments());
ListView PaysListView = (ListView)findViewById(R.id.EnterPays_PaysList);
PaysListView.setAdapter(adapter);
5. Adding / removing items to ListView (and its adapter)
Adapter is handled just like any other ArrayList, so adding new element to it is as simple as:
Payment testPayment = new Payment("Test", 13);
adapter.add(testPayment);
adapter.remove(testPayment);
6. Handle Remove Payment button click event
In an activity’s code, where ListView is displayed, add public method that will handle remove button click action. The method name has to be exactly the same as it was in pay_list_item.xml:
android:onClick="removePayOnClickHandler"
The method body is as follows:
public void removePayOnClickHandler(View v) {
Payment itemToRemove = (Payment)v.getTag();
adapter.remove(itemToRemove);
}
The Payment object was stored in ImageButton’s Tag element. Now it is enough to read it from Tag, and remove this item from the adapter.
7. Incorporate remove confirmation dialog window
Probably you need also make sure that user intentionally pressed the remove button by asking him additional question in confirmation dialog.
Dialogue
a) Create dialog’s id constant
This is simply dialog’s ID. it should be unique among any other dialog window that is handled by current activity. I set it like that:
protected static final int DIALOG_REMOVE_CALC = 1;
protected static final int DIALOG_REMOVE_PERSON = 2;
b) Build dialog
I use this method to build dialog window:
private Dialog createDialogRemoveConfirm(final int dialogRemove) {
return new AlertDialog.Builder(getApplicationContext())
.setIcon(R.drawable.trashbin_icon)
.setTitle(R.string.calculation_dialog_remove_text)
.setPositiveButton(R.string.calculation_dialog_button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
handleRemoveConfirm(dialogRemove);
}
})
.setNegativeButton(R.string.calculation_dialog_button_cancel, null)
.create();
}
AlertDialog builder pattern is utilized here. I do not handle NegativeButton click action – by default the dialog is just being hidden. If dialog’s confirm button is clicked, my handleRemoveConfirm callback is called and action is performed based on dialog’s ID:
protected void handleRemoveConfirm(int dialogType) {
if(dialogType == DIALOG_REMOVE_PERSON){
calc.removePerson();
}else if(dialogType == DIALOG_REMOVE_CALC){
removeCalc();
}
}
c) Show Dialog
I show dialog after my remove button click. The showDialog(int) is Android’s Activity’s method:
OnClickListener removeCalcButtonClickListener = new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_REMOVE_CALC);
}
};
the showDialog(int) method calls onCreateDialog (also defined in Activity’s class). Override it and tell your app what to do if the showDialog was requested:
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_REMOVE_CALC:
return createDialogRemoveConfirm(DIALOG_REMOVE_CALC);
case DIALOG_REMOVE_PERSON:
return createDialogRemoveConfirm(DIALOG_REMOVE_PERSON);
}
}
Take a look at this blog post I wrote on exactly this matter:
Create custom ArrayAdapter
There are comments that explain every action I make in the adapter.
Here is the explanation in short:
So lets for example take a row where you want to place a CheckBox, ImageView
and a TextView while all of them are clickable. Meaning that you can click the
row it self for going to another Actvity for more details on the row, check its
CheckBox or press the ImageView to perform another operation.
So what you should do is:
1. First create an XML layout file for your ListView row:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/cbCheckListItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp" />
<TextView
android:id="#+id/tvItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="item string" />
<ImageView
android:id="#+id/iStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#drawable/ic_launcher"
android:src="#drawable/ic_launcher" />
</LinearLayout>
2. Second in your java code define a ViewHolder, a ViewHolder
is designed to hold the row views and that way operating more quickly:
static class ViewHolder
{
TextView title;
CheckBox checked;
ImageView changeRowStatus;
}
3. Now we have to define CustomArrayAdapter, using the array adapter
we can define precisely what is the desired output for each row based on the content of this
row or it’s position. We can do so by overriding the getView method:
private class CustomArrayAdapter extends ArrayAdapter<RowData>
{
private ArrayList<RowData> list;
//this custom adapter receives an ArrayList of RowData objects.
//RowData is my class that represents the data for a single row and could be anything.
public CustomArrayAdapter(Context context, int textViewResourceId, ArrayList<RowData> rowDataList)
{
//populate the local list with data.
super(context, textViewResourceId, rowDataList);
this.list = new ArrayList<RowData>();
this.list.addAll(rowDataList);
}
public View getView(final int position, View convertView, ViewGroup parent)
{
//creating the ViewHolder we defined earlier.
ViewHolder holder = new ViewHolder();)
//creating LayoutInflator for inflating the row layout.
LayoutInflater inflator = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//inflating the row layout we defined earlier.
convertView = inflator.inflate(R.layout.row_item_layout, null);
//setting the views into the ViewHolder.
holder.title = (TextView) convertView.findViewById(R.id.tvItemTitle);
holder.changeRowStatus = (ImageView) convertView.findViewById(R.id.iStatus);
holder.changeRowStatus.setTag(position);
//define an onClickListener for the ImageView.
holder.changeRowStatus.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Toast.makeText(activity, "Image from row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
holder.checked = (CheckBox) convertView.findViewById(R.id.cbCheckListItem);
holder.checked.setTag(position);
//define an onClickListener for the CheckBox.
holder.checked.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
//assign check-box state to the corresponding object in list.
CheckBox checkbox = (CheckBox) v;
rowDataList.get(position).setChecked(checkbox.isChecked());
Toast.makeText(activity, "CheckBox from row " + position + " was checked", Toast.LENGTH_LONG).show();
}
});
//setting data into the the ViewHolder.
holder.title.setText(RowData.getName());
holder.checked.setChecked(RowData.isChecked());
//return the row view.
return convertView;
}
}
4. Now you need to set this adapter, as the adapter of your ListView.
this ListView can be created in java or using an XML file, in this case I’m using a list that was
defined in the XML file using the “list” id:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
ListView list = (ListView)findViewById(R.id.list);
CustomArrayAdapter dataAdapter = new CustomArrayAdapter(this, R.id.tvItemTitle, rowDataList);
list.setAdapter(dataAdapter);
}
5. Finally if we want to be able to press the row it self and not only a certain view in it
we should assign an onItemClickListener to the ListView:
list.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view,int position, long id)
{
Toast.makeText(activity, "row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
First, the way of adding listeners in xml using onClick="function" is deprecated. You need a ViewHolder class to link the button in the xml to your java code. Then you can implement onClickListener for that.
Inside your getView() implementation of CustomAdapter, you can try like below.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.xxxxx, null);
holder = new ViewHolder();
holder.invite = (Button) convertView.findViewById(R.id.button);
} else {
holder = (ViewHolder) convertView.getTag();
}
final int pos = position;
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleClick(pos);
}
});
}
class ViewHolder {
Button button;
}

Adding views programmatically in Android

Is it possible to add views to a different layout than the one called in setContentView() in the OnCreate()?
Im trying to use Zylincs ViewPager (found at http://www.zylinc.com/blog-reader/items/viewpager-page-indicator.html) and I have that part setup and working great.
What this leaves me with is 4 layouts. 1 is main.xml, and the other three are main_pg0.xml, main_pg1.xml, and main_pg3.xml
The three pages are where the content of each "page" is where main.xml contains the page viewer.
In the onCreate im using setContentView(main.xml).
What im trying to do now is be able to add textViews programically to some of the pages.
What I have right now is:
LayoutInflater factory = getLayoutInflater();
View layout = factory.inflate(R.layout.main_pg0, null);
View linearLayout = layout.findViewById(R.id.linearLayout);
TextView valueTV = new TextView(this);
valueTV.setText("hallo hallo");
valueTV.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
((LinearLayout) linearLayout).addView(valueTV);
This is as far as I've been able to get, which does not add the textview at all.
----EDIT----
Hoping to clarify a little more what im trying to do
Heres my code
Main.java
public class Main extends FragmentActivity implements PageInfoProvider {
public GlobalVars globalVars;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
globalVars = (GlobalVars) getApplicationContext();
MyPagerAdapter adapter = new MyPagerAdapter();
ViewPager myPager = (ViewPager) findViewById(R.id.viewpager);
myPager.setAdapter(adapter);
myPager.setCurrentItem(0);
ViewPagerIndicator indicator = (ViewPagerIndicator) findViewById(R.id.indicator);
myPager.setOnPageChangeListener(indicator);
indicator.init(0, adapter.getCount(), this);
Resources res = getResources();
Drawable prev = res.getDrawable(R.drawable.indicator_prev_arrow);
Drawable next = res.getDrawable(R.drawable.indicator_next_arrow);
indicator.setArrows(prev, next);
}
private class MyPagerAdapter extends PagerAdapter {
public int getCount() {
return 3;
}
public Object instantiateItem(View collection, int position) {
LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
int resId = 0;
switch (position) {
case 0:
resId = R.layout.main_pg0;
LoadVehicles();
break;
case 1:
resId = R.layout.main_pg1;
break;
case 2:
resId = R.layout.main_pg2;
break;
}
View view = inflater.inflate(resId, null);
((ViewPager) collection).addView(view, 0);
return view;
}
#Override
public void destroyItem(View arg0, int arg1, Object arg2) {
((ViewPager) arg0).removeView((View) arg2);
}
#Override
public void finishUpdate(View arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == ((View) arg1);
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
// TODO Auto-generated method stub
}
#Override
public Parcelable saveState() {
// TODO Auto-generated method stub
return null;
}
#Override
public void startUpdate(View arg0) {
// TODO Auto-generated method stub
}
}
public String getTitle(int position) {
String title = "--Untitled--";
switch (position) {
case 0:
title = "Page 0";
break;
case 1:
title = "Page 1";
break;
case 2:
title = "Page 2";
break;
}
return title;
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.zylinc.view.ViewPagerIndicator
android:id="#+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#EAEAEA"
android:paddingBottom="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="8dp" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffeaeaea" >
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_alignBottom="#+id/current"
android:background="#C5C5C5" />
<ImageView
android:id="#+id/current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:src="#drawable/indicator_current" />
</RelativeLayout>
<android.support.v4.view.ViewPager
android:id="#+android:id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
main_pg0.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF"
android:orientation="vertical"
android:id="#+id/linearLayout" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="main_pg0.xml"
android:textColor="#0C0C0C"
android:textSize="18sp" />
</LinearLayout>
What im trying to do is add a text view programmatically to main_pg0.xml ONLY, while leaving the other 2 pages alone
You haven't explicitly mentioned whether you're feeding Fragments to the ViewPager, but since you're working from the Zylinc implementation, I'm going to assume you are. As a matter of fact, I'll just use the provided example code to show you the idea.
The key to understanding how to realise what you're after is to get that the pages in the ViewPager are self-contained units in the form of Fragments. They are reusable, have their own life cycle, and can handle their own layouts. Hence, if you want to make runtime modifications to the layout of the pages, you will want to do this inside the Fragment(s) that make up these pages.
The following method can be found in the example code's ItemFragment class. I've simply added the TextView from your own code snippet to it. For demonstration purposes, I did hide the ListView that used to fill up all the space in the date_fragment.xml layout file.
#Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.date_fragment, container, false);
View tv = v.findViewById(R.id.text);
((TextView)tv).setText(readableDateFormat.format(date));
// changes below
TextView valueTV = new TextView(getActivity());
valueTV.setText("hallo hallo");
valueTV.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
((ViewGroup)v).addView(valueTV);
return v;
}
That will give you the "hallo hallo" text (you're a Dutchie? ;)) directly underneath the date in the page:
By the way, you might want to consider switching to Jake's ViewPagerIndicator. Although I haven't closely looked at the one you're currently using, Jake's seems more flexible to me and cleaner to work with. Up to you of course.
Edit: Okay, so you're not actually using fragments in the ViewPager, but are rather feeding it the layouts. That's fine, although it'll probably become harder to manage when the pages get more complex. Anyways, key to changing the page's layouts at runtime is still to do it whenever you build/inflate it. When using fragments, that would be in the overridden method, as shown above. In your case, however, you'll need to do it directly in instantiateItem(...).
I didn't actually type this out in an IDE, so please mind and minor syntactical errors.
public Object instantiateItem(View collection, int position) {
LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = null;
switch (position) {
case 0:
view = inflater.inflate(R.layout.main_pg0, null);
TextView valueTV = new TextView(getActivity());
valueTV.setText("hallo hallo");
valueTV.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
((ViewGroup)view).addView(valueTV);
break;
case 1:
view = inflater.inflate(R.layout.main_pg1, null);
// or perhaps better for readability: build the layout in a different method, passing in the root
buildSecondPageLayout(view);
break;
case 2:
view = inflater.inflate(R.layout.main_pg2, null);
buildThirdPageLayout(view);
break;
}
return view;
}
You have to add view in main view which is inflated layout.So just add one line.
LayoutInflater factory = getLayoutInflater();
View layout = factory.inflate(R.layout.main_pg0, null);
View linearLayout = layout.findViewById(R.id.linearLayout);
TextView valueTV = new TextView(this);
valueTV.setText("hallo hallo");
valueTV.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
((LinearLayout) linearLayout).addView(valueTV);
layout.addView(linearLayout);

Categories