Below is a link to the bug I am experiencing with my Android application. Rather than trying to explain it via a huge wall of text, I figured a simple video would be much more direct and easier to understand.
http://www.youtube.com/watch?v=9V3v854894g
I've been beating my head against a wall on this problem for a day and a half now. I only found that it could be solved by changing the XML layout just recently which makes absolutely no sense to me. I have no idea how to properly fix it, or a way to band-aid the problem since I need the nested layouts in my application.
Thank you everyone for all your help!
Here is the code:
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.AdapterView.OnItemSelectedListener;
public class Builder extends Activity {
private Spinner mCompSelect;
private Spinner mNameSelect;
private int[] mCompColorAsBuilt;
private int mComponent;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.builder);
mCompColorAsBuilt = new int[3];
//Attach our objects
mCompSelect = (Spinner) findViewById(R.id.component);
mNameSelect = (Spinner) findViewById(R.id.component_name);
//Attach an adapter to the top spinner
ArrayAdapter<CharSequence> a = ArrayAdapter.createFromResource(this, R.array.cc_components, android.R.layout.simple_spinner_item);
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mCompSelect.setAdapter(a);
//Create a listener when the top spinner is clicked
mCompSelect.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//Save the position
mComponent = position;
//Create a new adapter to attach to the bottom spinner based on the position of the top spinner
int resourceId = Builder.this.getResources().getIdentifier("component"+Integer.toString(mComponent)+"_color", "array", Builder.this.getPackageName());
ArrayAdapter<CharSequence> a = ArrayAdapter.createFromResource(Builder.this, resourceId, android.R.layout.simple_spinner_item);
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mNameSelect.setAdapter(a);
//Set the position of the bottom spinner to the saved position
mNameSelect.setSelection(mCompColorAsBuilt[mComponent]);
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
//Attach an adapter to the bottom spinner
int resourceId = this.getResources().getIdentifier("component"+Integer.toString(mComponent)+"_color", "array", this.getPackageName());
ArrayAdapter<CharSequence> b = ArrayAdapter.createFromResource(this, resourceId, android.R.layout.simple_spinner_item);
b.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mNameSelect.setAdapter(b);
mNameSelect.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//Save the position of the bottom spinner
mCompColorAsBuilt[mComponent] = position;
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Spinner
android:id="#+id/component"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="#+id/finish"
android:drawSelectorOnTop="true"
android:prompt="#string/component_spinner" />
<LinearLayout
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<Spinner
android:id="#+id/component_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawSelectorOnTop="true"
android:prompt="#string/component_name_spinner" />
</LinearLayout>
</RelativeLayout>
As a hack, try calling invalidate() on the affected Spinner. First, try after you call setSelection(). If that fails, try using postDelayed() on the Spinner to call invalidate() a bit later (e.g., 50ms).
In addition, I encourage you to create a demonstration project with two activities (or maybe just the one activity with two layouts) that illustrates this behavior, and post it and an explanation to http://b.android.com.
Related
I've followed different links and forums to solve this problem, but I couldn't solve it. So I think I am missing something important in my code but I don't get what.
The situation is the following. I'm trying to invoke some click listeners to three buttons. When I initialized them (I've tried in onCreateView() and in OnViewFragment() method, Also in OnCreate()) It's in a fragment. I attach my XML below (a part, for sure), I tried to invoke it using getActivity().findViewById(R.id.the_id_of_each_button) and also with
View view = inflater.inflate(R.layout.fragment_profile, container, false);
one_of_my_buttons =(Button) getActivity().findViewById(R.id.the_id_of_one_of_my_buttons);
I think It's because I've my buttons into some layouts, but I didn't find how to solve this.
My XML (Right now):
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".profileFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollableView">
<ImageView
android:id="#+id/profilePicture"
android:layout_width="0dp"
android:layout_height="225dp"
android:background="#color/azulito"
android:src="#drawable/default_prfile_picture"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/buttons_to_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:backgroundTint="#FFFFFF"
android:focusable="true"
android:tint="#color/azulito"
android:src="#drawable/ic_baseline_add_24"
app:backgroundTint="#color/azulito"
app:borderWidth="0.5dp"
app:layout_constraintBottom_toBottomOf="#id/profilePicture"
app:layout_constraintEnd_toEndOf="#id/profilePicture" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/edit_profile_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:focusable="true"
android:visibility="invisible"
android:layout_marginBottom="64dp"
android:backgroundTint="#FFFFFF"
android:src="#drawable/ic_baseline_image_24"
android:tint="#color/azulito"
app:backgroundTint="#color/azulito"
app:borderWidth="0.5dp"
app:fabSize="mini"
app:layout_constraintBottom_toTopOf="#id/buttons_to_edit"
app:layout_constraintEnd_toEndOf="#+id/buttons_to_edit"
app:layout_constraintStart_toStartOf="#+id/buttons_to_edit"
app:rippleColor="#color/azulito" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/edit_profile"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_marginEnd="8dp"
android:focusable="true"
android:layout_marginBottom="4dp"
android:backgroundTint="#FFFFFF"
android:src="#drawable/ic_baseline_edit_24"
app:backgroundTint="#color/azulito"
app:borderWidth="0.5dp"
app:fabSize="mini"
app:layout_constraintBottom_toTopOf="#id/edit_profile_picture"
app:layout_constraintEnd_toEndOf="#+id/edit_profile_picture"
app:layout_constraintStart_toStartOf="#+id/edit_profile_picture" />
<!-- here is more XML code, but nothing really relevant for this purposes-->
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
And my Java code (for this particular fragment because others are working fine):
package com.janus.janusapp;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.Toast;
/**
* A simple {#link Fragment} subclass.
* Use the {#link profileFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class profileFragment extends Fragment{
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private Animation rotateOpen ;
private Animation rotateClose ;
private Animation fromBottom ;
private Animation toBottom ;
//Here is where I declare those three buttons
private Button more_buttons;
private Button edit_profile_picture;
private Button edit_profile;
private boolean clicked = false;
/**====================================================================================================*/
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
public profileFragment() {
// Required empty public constructor
}
public static profileFragment newInstance(String param1, String param2) {
profileFragment fragment = new profileFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
/*===================The OnCreateMethod===================================*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
/*==================================================================================================
* Here are some functions used by the buttons, but there's not where it fails, so, I just mention it.
/*
private void deployMoreButtons();
private void setVisibility(boolean clicked);
private void setAnimation(boolean clicked);
private void setClickable(boolean clicked);
/*=====================================The onCreateView() Method====================================*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_profile, container, false);
rotateOpen = AnimationUtils.loadAnimation(getActivity(),R.anim.rotate_open_anim);
rotateClose = AnimationUtils.loadAnimation( getActivity(),R.anim.rotate_close_anim);
fromBottom = AnimationUtils.loadAnimation(getActivity(),R.anim.from_bottom_anim);
toBottom = AnimationUtils.loadAnimation( getActivity(),R.anim.to_bottom_anim);
/* I initialize my buttons right here, In the ways I tried before. When I use getActivity() it passes but the value of the burron seems to be null, and in the other way it just fails*/
more_buttons =(Button) getActivity().findViewById(R.id.buttons_to_edit);
edit_profile = (Button)getActivity().findViewById(R.id.edit_profile);
edit_profile_picture =(Button) view.findViewById(R.id.edit_profile_picture);
/* My button SetOnClickListeners, here is where my execution fails using getActivity()*/
more_buttons.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
deployMoreButtons();
}
});
edit_profile_picture.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Do Something
}
});
edit_profile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do Something
}
});
return view;
}
}
So what I'm doing wrong and what can I do? Thanks for your help.
As you declared the inflated layout for the fragment as "view", now if you want to call anything from the view so you have to use it as...
view.edit_profile
Here when you type view. Is means you are calling view from the layout you inflated as view.
you use Fragment and already Inflate. So
more_buttons =(Button) getActivity().findViewById(R.id.buttons_to_edit);
edit_profile = (Button)getActivity().findViewById(R.id.edit_profile);
edit_profile_picture =(Button) view.findViewById(R.id.edit_profile_picture);
to
more_buttons =(Button) view.findViewById(R.id.buttons_to_edit);
edit_profile = (Button) view.findViewById(R.id.edit_profile);
edit_profile_picture =(Button) view.findViewById(R.id.edit_profile_picture);
You are using activity reference to find the id of the buttons, but the button is in your Fragment xml which you have inflated and then you can find the id's of buttons defined in fragment xml using view as shown below
View view = inflater.inflate(R.layout.fragment_profile, container, false);
one_of_my_buttons =(Button) view.findViewById(R.id.the_id_of_one_of_my_buttons);
In onCreateView use
view.findViewById(YourId);
Okay, now maybe I'm going to gain some hate...
The class of the buttons I had are FloatingActionButton that's why they didn't work using view inflater. Now it works fine. I used the solutions everyone took here. Thanks.
I'm trying to populate a ListView with Strings (food item names) on the left side, and item count numbers on the right. I've made a CustomAdapter class (see below) which extends BaseAdapter after following some tutorials, where I could see (via print statements) that at least the getCount() method is called, but my getView() method doesn't get called and my ListView doesn't appear on my app. What am I doing wrong? I assume that it's because I don't know how to make getView() run in the right place. It works fine when I use a normal ArrayAdapter with an array of strings.
Here is how I tried populating the ListView in my MainActivity class:
CustomAdapter customAdapter = new CustomAdapter();
ListView lv1 = (ListView) findViewById(R.id.listView);
lv1.setAdapter(customAdapter);
customAdapter.notifyDataSetChanged();
Now, here is my CustomAdapter class:
import java.util.ArrayList;
import java.util.List;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class CustomAdapter extends BaseAdapter {
List<foodItem> mItemList = getDataForListView();
public CustomAdapter(List<foodItem> list){
this.mItemList = list;
// tried this as an alternative
}
#Override
public int getCount() {
return mItemList.size();
}
#Override
public foodItem getItem(int arg0) {
return mItemList.get(arg0);
}
#Override
public long getItemId(int arg0) {
return arg0;
}
#Override
public View getView(int arg0, View arg1, ViewGroup parent) {
System.out.println("Item list size: " + mItemList.size()); // doesn't show anything in console
if(arg1==null) {
LayoutInflater inflater = (LayoutInflater) LayoutInflater.from(parent.getContext());
arg1 = inflater.inflate(R.layout.listrow, parent ,false);
// R.layout.listrow is an XML file defined and formatted at 60sp tall
// as that is how tall I want each item in the ListView to be
}
TextView foodItem = (TextView)arg1.findViewById(R.id.foodTitle);
TextView likeCount = (TextView)arg1.findViewById(R.id.likeCount);
foodItem i = mItemList.get(arg0);
foodItem.setText(i.getItem());
likeCount.setText(i.getLikes());
return arg1;
}
public List<foodItem> getDataForListView() {
List<foodItem> itemList = new ArrayList<foodItem>();
// Test method populating with some data
for(int i=0; i<50; i++) {
itemList.add(new foodItem(i, "Item " + i));
}
return itemList;
}
}
I also tried adding my own constructor, but the result was the same. Any help would be greatly appreciated!
EDIT: The layout for my main 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="com.example.inventoryList.MainActivity" >
<ListView
android:id="#+id/foodOptions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp" />
</RelativeLayout>
Last, here is my listrow.xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="60sp"
android:orientation="horizontal"
android:padding="5sp" >
<TextView
android:id="#+id/foodTitle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:textSize="12sp"
android:text="123"
android:gravity="center" />
<TextView
android:id="#+id/likeCount"
android:layout_width="12sp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:text="#+string/likeCount"
android:textSize="10sp"
tools:ignore="SmallSp" />
</RelativeLayout>
You are Re-declaring List<foodItem> itemList in getDataForListView. This overrides the member variable. Change to
itemList = new ArrayList<>();
Also you should do this in the constructor, not a member method and prepending an m (so mItemList) to your member variables is good style and helps prevent this problem. So all together:
private class CustomAdapter extends BaseAdapter {
private List<foodItem> mItemList; //camel case isn't usually applied to classes os this should be FoodItem
public CustomAdapter() {
mItemList = new ArrayList<>();
for (...) {
...
}
}
...
}
Better still, pass the List to the Constructor:
private class CustomAdapter extends BaseAdapter {
private List<FoodItem> mItemList;
public CustomAdapter(List<FoodItem> itemList) {
mItemList = itemList;
}
...
}
I fixed it. My Main Activity has three tabs, and for each I set a tab listener that changes what's in the adapter. I had assumed that these tab listeners wouldn't be invoked upon starting the app, but apparently that was not true. In consequence, from things I had commented out in testing, I had been called setAdapter() and passing in an empty adapter.
Thanks for the help all, sorry that it turned out to be a mistake in code that I hadn't provided.
I had the similar problem, and I resolved by putting
adapter.notifyDataSetChanged();
OUTSIDE of my try-catch block.
Implement a constructor for your adapter so you can pass an items list to it:
public CustomAdapter(List<foodItem> itemList) {
this.itemList = itemList;
}
Create your list beforehand and pass it to the adapter constructor:
ArrayList<foodItem> items = buildItemsList();
CustomAdapter customAdapter = new CustomAdapter(items);
ListView lv1 = (ListView) findViewById(R.id.listView);
lv1.setAdapter(customAdapter);
If you need a mutable adapter, implement a method (in the adapter) to add items and notify data set changes after that:
public void add(foodItem item) {
itemList.add(item);
notifyDataSetChanged();
}
You can do the same to remove and update items, just remember to notify changes so the list view will refresh.
[EDIT]: Thank you for all the meaningful answers, the problem is now solved, thank to your help. Similar issue: Android: I am unable to have ViewPager WRAP_CONTENT
I'm trying to implement the UI of my app : I want a ListView with a ViewPager in each row.
Here are my files :
MainActivity.java
package com.condi;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.Menu;
public class MainActivity extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setListAdapter(new CardListAdapter(this, getFragmentManager()));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
CardListAdapter.java
package com.condi;
import java.util.List;
import android.app.FragmentManager;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public class CardListAdapter extends BaseAdapter {
private Context context;
private FragmentManager fragmentManager;
private List<Profile> profiles;
CardListAdapter(Context context, FragmentManager fragmentManager) {
this.context = context;
this.fragmentManager = fragmentManager;
this.profiles = new DatabaseHelper(context).getProfiles();
}
#Override
public int getCount() {
return profiles.size();
}
#Override
public Profile getItem(int position) {
return profiles.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = View.inflate(context, R.layout.profile_card, null);
CardPagerAdapter mCardPagerAdapter = new CardPagerAdapter(
fragmentManager);
ViewPager viewPager = (ViewPager) convertView.findViewById(R.id.pager);
viewPager.setAdapter(mCardPagerAdapter);
viewPager.setId(R.id.pager);
return convertView;
}
}
profile_card.xml (issue came from wrap_content).
<?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="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:background="#drawable/card_background"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</FrameLayout>
CardPagerAdapter.java
package com.condi;
import android.app.Fragment;
import android.app.FragmentManager;
import android.support.v13.app.FragmentPagerAdapter;
public class CardPagerAdapter extends FragmentPagerAdapter {
public CardPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new Fragment1();
case 1:
return new Fragment2();
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
Fragment1.java
package com.condi;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment1 extends Fragment {
public Fragment1() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.preference_category, container, false);
}
}
Fragment2.java
package com.condi;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment2 extends Fragment {
public Fragment2() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.preference_category, container, false);
}
}
The database that furnishes the profiles is working well.
The problem is that I am getting an empty list with the correct number of item, but no ViewPager displayed at all. screenshot of my app
What am I doing wrong ?
Try to change your ViewPager's height in xml. wrap_content does not work.
To make is clear, its a bad practice to use viewpagers inside a list view as this design hits the ui performance. The best way to handle this problem is:
To do manual inflation of viewpagers and add to a layout.
Add a unique id to each viewpager so that it is identified uniquely by the system.
Extend a custom viewpager and onmeasure() measure the child layout inflated at the page selected. You can do this by setting a callback in your custom viewpager and trigger the callback from viewpagers onPageScrolled listener, passing the position to identify the child layout.
In your onMeaure() method measure the child layout height and set it as the viewpagers height using super.onMeasure() passing the newly measured specs.
Key points to make a note of :
N.B. Set a unique id to the viewpager inflated by you.
try this problem in layout height
<?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="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="4dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:background="#drawable/card_background"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</FrameLayout>
It's a bad practice to use wrap_content for your viewpager's height. You can use match_parent or a static dp for your viewpager's height.
<?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="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:background="#drawable/card_background"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</FrameLayout>
If it's a must to use wrap_content for your viewpager's height you can override your viewpager's onMeasure() and calculate height. Here's an example below about it.
Android: I am unable to have ViewPager WRAP_CONTENT
I hope this'll help you.
ViewPager doesn’t support wrap_content as it stands now because it doesn’t load all of its children at the same time, meaning it can’t get an appropriate measurement. You must fix View's height in your xml or create a custom ViewPager (read more at here) and use it in your xml
Try using a fixed height ViewPager inside the ListView.ViewPager does not support wrap_content
I am working on an app that works similar to the default text messaging app on all Android phones. My problem is selecting more than one user to send an SMS message to. What I have done so far is stored my contacts as listview items with check boxes. Now I just need to get the phone numbers from the selected contacts.
So what I am having trouble on doing.
1) Pulling a phone number from the contacts displayed in my listview
2)displaying that number in a textview in a new activity
Sorry if my code is difficult to understand, please ask if you need clerification.
This is the XML in which the listview is shown, called contact_manager.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" >
<ListView
android:id="#+id/contactList"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="#+id/showInvisible"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/showInvisible" />
</LinearLayout>
This is my Activity that calls everything together.
public final class ContactManager extends Activity {
public static final String TAG = "ContactManager";
private ListView mContactList;
private boolean mShowInvisible;
private Button mShowInvisibleControl;
/**
* Called when the activity is first created. Responsible for initializing
* the UI.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "Activity State: onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.contact_manager);
// Obtain handles to UI objects
mContactList = (ListView) findViewById(R.id.contactList);
mShowInvisibleControl = (Button) findViewById(R.id.showInvisible);
// Initialize class properties
mShowInvisible = false;
// mShowInvisibleControl.setChecked(mShowInvisible);
mShowInvisibleControl.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
}
});
populateContactList();
}
/**
* Populate the contact list based on account currently selected in the
* account spinner.
*/
private void populateContactList() {
// Build adapter with contact entries
Cursor cursor = getContacts();
String[] fields = new String[] { ContactsContract.Data.DISPLAY_NAME };
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.contact_entry, cursor, fields,
new int[] { R.id.contactEntryText });
mContactList.setAdapter(adapter);
}
/**
* Obtains the contact list for the currently selected account.
*
* #return A cursor for for accessing the contact list.
*/
private Cursor getContacts() {
// Run query
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] { ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME };
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '"
+ (mShowInvisible ? "0" : "1") + "'";
String[] selectionArgs = null;
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC";
return managedQuery(uri, projection, selection, selectionArgs,
sortOrder);
}
contact_entry.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<CheckBox
android:id="#+id/contactEntryText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#+id/contactEntryText" />
</LinearLayout>
<?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" >
<ListView
android:id="#+id/contactList"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="#+id/showInvisible"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/showInvisible" />
</LinearLayout>
This is my invite_text.xml This is actually the text view i want to input the numbers into so i can send a mass text message.
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/contacts"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:gravity="center"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:text="#string/contacts"
android:textAppearance="?android:attr/textAppearanceLarge" />
<!-- android:textColor="#fff" android:background="#drawable/header" for header background -->
<Button
android:id="#+id/contactsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="#string/contacts" />
</RelativeLayout>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/enter_contact"
android:textAppearance="?android:attr/textAppearanceMedium" />
<AutoCompleteTextView
android:id="#+id/contactnumber"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="#string/to" >
<requestFocus />
</AutoCompleteTextView>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/message_to_send"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="#+id/invite_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/message_join" />
<Button
android:id="#+id/sendtxt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="doLaunchContactPicker"
android:text="#string/send_txt" />
</LinearLayout>
</ScrollView>
If you need me to post any more information, please ask.
I have not looked at your code but here is the mechanism which worked for me:
1. Put setOnCheckedChangeListener to your checkbox.
2. If Checkbox is Checked then add that contact to arraylist.
3. If CheckBox is unchecked then remove that contact from arraylist.
4. Start your contact list activity with startActivityForResult() and override onActivityResult().
5. Before leaving contact activity set your selected contacts in intent.
6. Receive the selected contacts in your activity.
7. Now you have selected contacts which you can show in TextView.
Note: You need to use custom list adapter for this:
Custom List Adapter:
public class YourAdapterName extends BaseAdapter{
private Context mContext;
private ArrayList<string> mValuestoShow;
/**
* Constructor to be called to initialize adapter with values.
* #param context
* #param vector
*/
public YourAdapterName(Context context, ArrayList<string> contacts){
mContext = context;
mValuestoShow = contacts;
}
public int getCount() {
if(null != mValuestoShow){
return mValuestoShow.size();
}
return 0;
}
public Object getItem(int position) {
if(position < mValuestoShow.size())
return mValuestoShow.get(position);
else
return null;
}
public long getItemId(int position) {
return 0;
}
/**
* This method can be override to enable/disable particular list row.
*/
#Override
public boolean isEnabled(int position) {
//Write your code here......
return super.isEnabled(position);
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
LayoutInflater li =(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = li.inflate(R.layout.contact_list_layout, null);
holder = new ViewHolder();
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.checkbox = (CheckBox)convertView.findViewById(R.id.checkbox);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
holder.name.setText(text goes here ....);
holder.checkbox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if ( isChecked )
//Add contact...
else
//Remove contact.
}
});
return convertView;
}
class ViewHolder {
TextView name;
CheckBox checkbox;
}
}
your_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp" >
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="50dp"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#android:color/black" />
<CheckBox
android:id="#+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
</RelativeLayout>
Please follow below steps to make it working:
->Have a boolean array, of size equal to cursor. This array will represent checked state of contact.
->In xml make checkbox unclickable and unfocusable.
->setOnItemClickListener on ListVIew and in onItemClick method toggle boolean array's value on position, item selected.
->Set OnClickListener on Button, and onClick method of listener fetch numbers from cursor by follosing:
ArrayList<String> numbers=new ArrayList<String>();
cursor.moveToFirst();
for(int i=0;i<cursor.getCount;i++)
{
cursor.moveToNext();
if(selected[i])
{
//fetch contact from cursor
//add number to numbers
}
}
//Use ArrayList numbers to send Contact, it doesnot seems that you can append multiple numbers to same message, in the case send messages to numbers by a loop, see following thread:
Sending sms to multiple people in android
amd
Unable to send sms using SMSManager in Android
There is no stort ans to your question. You have to write your own adapter and have support for multi selection. I had replied on SO to similar problem (If this helps OK else let me know and I can explain in detail):
Android listview toggle button
android : checkboxes in a ListView ( strange behaviour of selected element)
I'm just going to post a strategy answer. Use hashmap for the list adapter. Use a key that is not displayed to store the phone number to call. Allow multiple selection in the listview. Use the stored phone numbers from the selected entries.
I suggest you to look at the tutorial that I have written last month at this link to manage the selection of the checkbox in the list (it helps you to keep states and retrieves items by ids).
I think that it's better if you manage your list by id but displaying to user the contact name. After that you can pass your ids and send them with the mechanisms that jeet has told you.
I think the below code will give the results as what you expected...
Your main class will lokk like the below...
import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
public class GetSelectedContacts extends Activity{
int CONTACTS_REQUEST_CODE =1;
Activity thisActivity;
ArrayList<String> selectedConatcts;
LinearLayout contactdisp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
thisActivity = this;
Button btn = (Button)findViewById(R.id.btn_selectContact);
contactdisp = (LinearLayout)findViewById(R.id.lnr_contactshow);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(thisActivity,ListActivitySampleActivity.class);
startActivityForResult(intent, CONTACTS_REQUEST_CODE);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(data!=null){
Bundle bundle = data.getExtras();
if(requestCode ==1){
selectedConatcts = bundle.getStringArrayList("sel_contacts");
Log.v("", "Selected contacts-->"+selectedConatcts);
if(selectedConatcts.size()<0){
}else{
for(int i =0;i<selectedConatcts.size();i++){
LinearLayout lnr_inflate = (LinearLayout)View.inflate(thisActivity, R.layout.contacts_inflate, null);
EditText edt = (EditText)lnr_inflate.findViewById(R.id.edt_contact);
edt.setText(selectedConatcts.get(i));
contactdisp.addView(lnr_inflate);
}
}
}
}
}
}
The contacts selection class like
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
public class ListActivitySampleActivity extends Activity {
static ContentResolver cr;
String[] phone_nos;
ArrayList<String> selectedContacts = new ArrayList<String>();
Activity thisActivity;
Button btn;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
thisActivity = this;
final ListView lst = (ListView)findViewById(R.id.listView1);
populateContact();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(thisActivity, android.R.layout.simple_list_item_multiple_choice, phone_nos);
lst.setAdapter(adapter);
lst.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
lst.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
}
});
final int len = lst.getCount();
final SparseBooleanArray checked = lst.getCheckedItemPositions();
btn = (Button)findViewById(R.id.btn_send);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i = 0; i < len; i++)
if (checked.get(i)) {
selectedContacts.add(phone_nos[i]);
//you can you this array list to next activity
/* do whatever you want with the checked item */
}
Bundle bundle = new Bundle();
bundle.putStringArrayList("sel_contacts", selectedContacts);
Intent contactIntent = new Intent();
contactIntent.putExtras(bundle);
setResult(1, contactIntent);
thisActivity.finish();
// Log.v("", "selected-->"+selectedContacts);
}
});
}
private void populateContact(){
Uri myContacts = ContactsContract.CommonDataKinds.Phone.CONTENT_URI ;
Cursor mqCur = managedQuery(myContacts, null, null, null, null);
phone_nos = new String[mqCur.getCount()];
int i =0;
if(mqCur.moveToFirst())
{
do
{
String phone_no = mqCur.getString(mqCur
.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
phone_nos[i] = phone_no;
i++;
}
while(mqCur.moveToNext());
}
}
}
I'm trying to create an element within a ListView which displays a few TextViews and a custom view in which I plan to draw to the canvas by overriding onDraw(). The method I'm attempting works fine with just the TextViews, but fails when I try and use the custom element (Graph), so the problem definitely lies here.
I have the following XML file which I use as the layout for each ListView item:
year_row.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/year_row_linear"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="6dp"
>
<TextView android:id="#+id/year_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dip"
android:textColor="#android:color/white"
/>
<com.android.gradetracker.Graph android:id="#+id/year_graph"
android:layout_width="fill_parent"
android:layout_height="50dip"
/>
<TextView android:id="#+id/year_average"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
/>
<TextView android:id="#+id/year_progress"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
My Graph class is as follows:
Graph.java:
package com.android.gradetracker;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
public class Graph extends View {
public Graph(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
// to test if it works
canvas.drawColor(Color.WHITE);
super.onDraw(canvas);
}
}
My main class which sets up the elements of the ListView then contains the following code:
private ArrayList <HashMap<String,Object>> list = new ArrayList <HashMap<String,Object>>();
private SimpleAdapter adapter;
private ListView listView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.listview);
listView.setClickable(true);
String[] from = {"name", "graph", "average", "progress"};
int[] to = new int[] {R.id.year_name, R.id.year_graph, R.id.year_average, R.id.year_progress};
adapter = new SimpleAdapter(this.getApplicationContext(), list, R.layout.year_row, from, to);
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
public void addYear(String name) {
Graph graph = new Graph(this);
HashMap<String,Object> map = new HashMap<String,Object>();
map.put("name", name);
map.put("graph", graph);
map.put("average", "--%");
map.put("progress", "--/--%");
list.add(map);
adapter.notifyDataSetChanged();
}
As I said, removing all references to the Graph class, the application works fine. I'm just having trouble getting Graph to display.
Logcat gives the error:
08-04 17:09:50.415: ERROR/AndroidRuntime(397): android.view.InflateException: Binary XML file line #16: Error inflating class com.android.gradetracker.Graph
Thanks for any help in advance.
Okay, the main problem seemed to be that SimpleAdaptor does not allow you to use custom views. I had to create a custom adaptor which extends BaseAdapter in order to display the custom view, with the following code for the getView() method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.year_row, parent, false);
}
TextView name = (TextView) convertView.findViewById(R.id.year_name);
Graph graph = (Graph) convertView.findViewById(R.id.year_graph);
TextView average = (TextView) convertView.findViewById(R.id.year_average);
TextView progress = (TextView) convertView.findViewById(R.id.year_progress);
ArrayList<Object> row = list.get(position);
name.setText(String.valueOf(row.get(0)));
graph = (Graph)row.get(1);
average.setText(String.valueOf(row.get(2)));
progress.setText(String.valueOf(row.get(3)));
return convertView;
}
You need a constructor in your Graph class.
Start By calling super
Change the onDraw function of the Graph class
#Override
protected void onDraw(Canvas canvas) {
//start with super
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
}