i am new to android development, and from java background , am developing an android app where i have to display of invites which user have got, so the layout will be like below
here invite response view (yes, no , may) will be shown on click of each invite view, but i want the response view to get closed or visibility = Gone when user clicks on another view. currently response views are shown once clicked on invite view.
so to solve this issue, i have added id (inviteId) to each response view as below
final LinearLayout second = (LinearLayout) inviteView.findViewById(R.id.hidden);
second.setId((int) currentInviteId);
now i am trying to get firstly opened response view id when user clicks on next invite view and trying to set the first response view to "GONE"
public class InvitationFragment extends Fragment {
private List<String> eventName = new ArrayList<>();
private List<Long> eventId = new ArrayList<>();
private List<String> eventPlace = new ArrayList<>();
private List<EventMO> eventMOs = new ArrayList<>();
private List<UserMO> userMO = new ArrayList<>();
private Context context;
private UserOccasions userOccasions;
private UserDelegate userDelegate = new UserDelegate();
private EventDelegates eventDelegates = new EventDelegates();
private Gson gson = new Gson();
private ProgressDialog prgDialog;
private EventMO eventMO;
// private long compareEventId;
private SharedPreferences prefs;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final View view = inflater.inflate(R.layout.invitation_tab, container, false);
context = getActivity().getApplicationContext();
prgDialog = new ProgressDialog(getActivity());
eventId.clear();
eventName.clear();
eventPlace.clear();
// Set Progress Dialog Text
prgDialog.setMessage("Please wait...");
// Set Cancelable as False
prgDialog.setCancelable(false);
prgDialog.show();
DatabaseHelper dbHelper = new DatabaseHelper(context);
final UserMO userMO = dbHelper.getRingeeUserData(1);
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... arg0) {
return eventDelegates.getAllEventForUser(userMO, context);
}
#Override
protected void onPostExecute(String eventlists) {
if (eventlists != "null") {
eventMOs = gson.fromJson(eventlists, new TypeToken<List<EventMO>>() {
}.getType());
Toast.makeText(context, "total items of eventMo" + eventMOs.size(), Toast.LENGTH_LONG).show();
for (EventMO eventMO : eventMOs) {
eventName.add(eventMO.getText());
eventId.add(eventMO.getEventId());
eventPlace.add(eventMO.getPlace());
}
DatabaseHelper dbHelper = new DatabaseHelper(context);
//long totalInsertion = dbHelper.insertUserRelationTable(userMOs);
prgDialog.dismiss();
//Toast.makeText(context, "total userMos size " + userMOs.size() + "total db insertion size " + totalInsertion, Toast.LENGTH_LONG).show();
ListView occasionView = (ListView) view.findViewById(R.id.invitation_list_view);
userOccasions = new UserOccasions();
occasionView.setAdapter(userOccasions);
occasionView.setItemsCanFocus(false);
occasionView.setTextFilterEnabled(true);
occasionView.setOnItemClickListener(occasionView.getOnItemClickListener());
}
}
}.execute(null, null, null);
return view;
}
private class UserOccasions extends BaseAdapter {
LayoutInflater mInflater;
TextView eventNameTxtV, eventPlaceTxtV;
UserOccasions() {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return eventMOs.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return 0;
}
// show list values name and mobile number in contact page
#Override
public View getView(final int position,View inviteView, ViewGroup parent) {
if (inviteView == null) {
inviteView = mInflater.inflate(R.layout.invitation, null);
}
EventMO eventMO = eventMOs.get(position);
final long currentEventId = eventMO.getEventId();
eventNameTxtV = (TextView) inviteView.findViewById(R.id.invitation_title);
eventPlaceTxtV = (TextView) inviteView.findViewById(R.id.invitation_place);
eventNameTxtV.setText(eventMO.getText());
eventPlaceTxtV.setText(eventMO.getPlace());
inviteView.setTag(position);
View v = inviteView.findViewById(R.id.invitation_single);
final LinearLayout first = (LinearLayout) inviteView.findViewById(R.id.invitation_single);
Button yesBtn = (Button) inviteView.findViewById(R.id.yesbutton);
Button noBtn = (Button) inviteView.findViewById(R.id.nobutton);
Button mayBeBtn = (Button) inviteView.findViewById(R.id.buttonmaybe);
final LinearLayout second = (LinearLayout) inviteView.findViewById(R.id.hidden);
second.setId((int) currentEventId);
// to store current event id into shared preference, to compare event ids and close child layout if ids are differents
prefs = context.getSharedPreferences(InvitationFragment.class.getSimpleName(), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong("compareEventId", currentEventId);
editor.commit();
yesBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
second.setVisibility(View.GONE);
final String response = "yes";
final EventMO event = new EventMO();
event.setIs_Attend(response);
new AsyncTask<Void, Void, String>() {
protected String doInBackground(Void... arg0) {
return eventDelegates.updateEvent(event, context);
}
}.execute(null, null, null);
}
});
noBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
second.setVisibility(View.GONE);
final String response = "no";
final EventMO event = new EventMO();
event.setIs_Attend(response);
new AsyncTask<Void, Void, String>() {
protected String doInBackground(Void... arg0) {
return eventDelegates.updateEvent(event, context);
}
}.execute(null, null, null);
}
});
mayBeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
second.setVisibility(View.GONE);
final String response = "maybe";
final EventMO event = new EventMO();
event.setIs_Attend(response);
new AsyncTask<Void, Void, String>() {
protected String doInBackground(Void... arg0) {
return eventDelegates.addEvent(event, context);
}
}.execute(null, null, null);
}
});
first.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View invitationView) {
final long compareEventId = prefs.getLong("compareEventId", 0);
final long currentEventId = second.getId();
if(compareEventId != 0 && compareEventId != currentEventId){
LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View inflatedView = inflater.inflate(R.layout.invitation, null);
final View inviteResponseView = (View) inflatedView.findViewById((int) compareEventId);
inviteResponseView.setVisibility(View.GONE);
}
switch (invitationView.getId()) {
case R.id.invitation_single:
second.setVisibility(View.VISIBLE);
break;
}
}
});
return inviteView;
}
}
}
but inviteResponseView is always returning null. need direction to solve this functionality. Thanks for your valuable response.
EDIT :-
my layout xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/invitation_single"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="?android:dividerVertical"
android:dividerPadding="5dp"
android:showDividers="middle"
tools:context=".MainActivity">
<ImageButton
android:id="#+id/image"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/ic_action_event" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:clickable="false"
android:focusable="true"
android:orientation="vertical">
<TextView
android:id="#+id/invitation_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="0dp"
android:paddingTop="3dp"
android:textColor="#color/black"
android:textSize="18sp" />
<TextView
android:id="#+id/invitation_place"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="0dp"
android:textColor="#color/black"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:id="#+id/hidden"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginLeft="-270dp"
android:layout_marginTop="60dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingTop="1dp"
android:visibility="gone"
android:weightSum="3">
<Button
android:id="#+id/yesbutton"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="7dp"
android:layout_weight="1"
android:background="#color/blue"
android:text="Yes"
android:textColor="#color/black"></Button>
<Button
android:id="#+id/nobutton"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="25dp"
android:layout_weight="1"
android:background="#color/blue"
android:text="No"
android:textColor="#color/black"></Button>
<Button
android:id="#+id/buttonmaybe"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="25dp"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:background="#color/blue"
android:text="Maybe"
android:textColor="#color/black"></Button>
</LinearLayout>
</LinearLayout>
Why don't you use an onFocusChangeListener ?
First of all:
Set in your invitationView layout .xml file (the one which contains the buttons yes, no, maybe) like this:
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
Note: You must also do this to your parent layout (the layout of the Activity) so when the user touch outside the invitationView, the parent layout will catch the focus.
Then, what you have to do is to set the invitationView.setOnFocusChangeListener to your invitationView, inside your Activity or inside your listAdapater (or recyclerViewAdapter) if you are using it to the invitationView (what I highly recommend).
Like this:
invitationView.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus)
{
inviteResponseView.setVisibility(view.GONE)
}else
inviteResponseView.setVisibility(view.VISIBLE)
}
});
Recommendation:
Recommend you to use a recyclerView instead of fragments to make this job, it pretty easy to use and memory usage efficient. Now it supports most of the devices with the design support library that already has a bunch of tutorials, like the linked ones.
I recommend you use some third-party dependency in your project.
As a matter of fact, nhaarman's ListViewAnimations can do that very well. Download the demo in Play Store and check it.
There's a fully customizable ListView that can expand on user click and show more items. Plus, it supports very cool animations. I've used it in one of my projects and it's very good. You can even set the maximum number of views that can be expanded at once.
In your BaseAdapter add
private boolean showActionView = false;
private int showActionViewFor;
public void showActionView(boolean show, int position) {
showActionView = show;
showActionViewFor = position;
notifyDataSetChanged();
}
Then in your BaseAdapter's getView() add
if(showActionView && position == showActionViewFor) {
second.setVisibility(View.VISIBLE);
} else {
second.setVisibility(View.INVISIBLE);
//Don't use View.GONE
}
Then in your Fragment's onCreateView(), add
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mAdapter.showActionView(true, position);
}
});
Bam! It should work as you need. Whenever you need to hide all action views just call
mAdapter.showActionView(false, 0);
UPDATE:
You need to remove your
first.setOnClickListener(new View.OnClickListener() {}
from your Adapter.
EXTRA: To improve your ListView performance, you should consider implementing the View Holder pattern.
Related
Hello everyone Im having an issue with making a dialog appear from an onClick listener from within a listview item. The dialogs were appearing and working fine within my application before I made certain methods in the activity that calls for the listview adapter static so that I could call those methods from the adapter. I am getting the error of:
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
from the call to:
alertDialog.show();
Before you tell me to go lookup this issue (because I have discovered that it is common) I have. And I have found solutions but none of them work for me. I haven't found anyone else having this issue within a custom listview adapter.
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getContext());
was working fine until the listview and certain methods were made static in the Services Acivity.
DancerAdpater.java
public class DancerAdapter extends ArrayAdapter<OneDancer> {
int type;
private static FragmentManager fragmentManager;
static Context context;
static public Integer count=1;
public DancerAdapter(Context context, ArrayList<OneDancer> dancers, int type) {
super(context, 0, dancers);
this.type=type;
this.context=context;
}
public DancerAdapter(Context context, ArrayList<OneDancer> dancers, int type,FragmentManager fragmentManager) {
super(context, 0, dancers);
this.type=type;
this.fragmentManager = fragmentManager;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
final OneDancer oneDancer = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
if(type==3) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_services, parent, false);
final Button button4 = (Button)convertView.findViewById(R.id.button4);
final TextView counterTextView = (TextView)convertView.findViewById(R.id.textView10);
final Button button6 = (Button)convertView.findViewById(R.id.button6);
final Button button5 = (Button)convertView.findViewById(R.id.button5);
final Button button8 = (Button)convertView.findViewById(R.id.button8);
final Button button9 = (Button)convertView.findViewById(R.id.button9);
final Button button10 = (Button)convertView.findViewById(R.id.button10);
//Start Suite Button
button8.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
// set title
alertDialogBuilder.setTitle("Confirm - Hold for Suite?");
// set dialog message
alertDialogBuilder
.setMessage("Confirm hold suite for " + oneDancer.name + "?")
.setCancelable(false)
.setPositiveButton("Yes",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("action", "makeVip");
params.put("name", oneDancer.name);
//params.put("TimeOut", );
Log.v("SignInActivity","Girl Made UnAvailable Response Query");
client.post("http://peekatu.com/apiweb/girlList.php", params,
new AsyncHttpResponseHandler() {
#Override
public void onSuccess(String response) {
Log.v("response", response);
//responseString = response;
//parseDancerList(response);
button5.setVisibility(View.GONE);
button4.setVisibility(View.GONE);
button8.setVisibility(View.GONE);
button6.setVisibility(View.VISIBLE);
button9.setVisibility(View.VISIBLE);
button10.setVisibility(View.VISIBLE);
counterTextView.setVisibility(View.VISIBLE);
Services.refresh();
if (response.indexOf("OK") > -1) {
Toast.makeText(getContext(),
"VIP Dance has began for " + oneDancer.name,
Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Throwable error, String content) {
Log.v("response", "response failed network error");
//waitncall(true);
}
});
dialog.cancel();
}
})
.setNegativeButton("No",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
});
item_services.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="wrap_content"
android:background="#3d87d5">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name"
android:id="#+id/tvName"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:textSize="20dp"
android:textStyle="bold" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tvAvail"
android:layout_gravity="center_vertical"
android:layout_weight=".1"
android:src="#drawable/online"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/feature_button"
android:id="#+id/button5"
android:layout_gravity="center_vertical|right" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#string/start_dance"
android:id="#+id/button4"
android:layout_gravity="center_vertical"
android:background="#005906"
android:layout_weight="1"
/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="+30 Mins"
android:id="#+id/button10"
android:layout_weight="1"
android:visibility="gone" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="+60 Mins"
android:id="#+id/button9"
android:layout_weight="1"
android:visibility="gone" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Start Suite"
android:id="#+id/button8"
android:layout_weight="1"
android:background="#fdd32b" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="00"
android:id="#+id/textView10"
android:visibility="gone"
android:layout_weight="1" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#string/stop_dance"
android:id="#+id/button6"
android:background="#ab0000"
android:visibility="gone"
android:layout_weight="1" />
</LinearLayout>
Services.java
public class Services extends Activity {
static Context context;
static private ListView listView ;
static private String responseString;
static String responseString2;
static ArrayList<OneDancer> oneDancerArrayList = new ArrayList<OneDancer>();
public DancerAdapter adapter;
Button addDancer;
Button loginDancer;
EditText dancerName;
EditText nameInput;
Integer count=0;
static DialogFragment newFragment;
public View rootView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_in);
Services.context = getApplicationContext();
if(adapter!=null){
adapter.clear();
}
View rootView = null;
View currentFocus = getWindow().getCurrentFocus();
if (currentFocus != null)
rootView = currentFocus.getRootView();
Log.v("SignInActivity", "Activity has began");
// Declare Listview
listView = (ListView) findViewById(R.id.listView);
// Assign adapter to ListView
listView.setAdapter(adapter);
//Call to Database and retrieve girl list
getGirlList();
}
static public void refresh(){
getGirlList();
adapter.notifyDataSetChanged();
}
static public void getGirlList() {
Log.v("SignInActivity","getGirlList has started");
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("action", "getDancers");
Log.v("SignInActivity","Girl List Response Query");
client.post("http://peekatu.com/apiweb/girlList.php", params,
new AsyncHttpResponseHandler() {
#Override
public void onSuccess(String response) {
Log.v("response", response);
responseString = response;
parseDancerList(response);
Log.v("SignInActivity", response);
Log.v("SignInActivity","Girl List Response" + responseString);
}
#Override
public void onFailure(Throwable error, String content) {
Log.v("response", "response failed network error");
//waitncall(true);
}
});
Log.v("SignInActivity","getGirlList ended");
}
// Parsing of private messages
static public void parseDancerList(String response) {
Log.v("SignInActivity","parseDancerList");
XMLParser parser = new XMLParser();
Document doc = parser.getDomElement(response); // getting DOM element
NodeList nl = doc.getElementsByTagName("DANCERS");
oneDancerArrayList.clear();
Log.v("response ", "Dancer Count " + nl.getLength());
// looping through all item nodes <item>
for (int i = 0; i < nl.getLength(); i++) {
// creating new HashMap
//adapter = new DancerAdapter(context,oneDancerArrayList,3);
// adapter = new DancerAdapter(this,oneDancerArrayList,3);
Element e = (Element) nl.item(i);
Log.v("response ", "Dancers " + parser.getValue(e, "NAME"));
OneDancer newDancer = new OneDancer(parser.getValue(e,"POSITION"),
parser.getValue(e,"NAME"),
parser.getValue(e,"AVAILABLE"),
null,
null,
null,
null,
null,
null,"0");
adapter.addAll(newDancer);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
I have tried this and Services.this as well as getApplicationContext() and none of these solutions are working for me. Please help. Thank you.
You can follow this step:
Create a global ArrayList for adapter data source and initialize adapter with this arraylist(initialize it before using it) in onCreate() method
When new data is available clear previous data of ArrayList by calling ArrayList.clear() then add all new data to that Arraylist and call adapter.notifyDataSetChanged();
By this way you can avoid using this in static context.
Edited:
Use the Context to create AlertDialog that was passed to constructor.
Declare a Context variable in Adapter class and initialize it inside the constructor with the context object you are passing as parameter.
Then use that context object to initialize the AlertDialog.
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getContext());
Use context variable that you initialized in contructor instead of getContext()
I have implemented both a ViewHolder and a convertView in my listView.
My listView is populated by a custom adapter, with a list of bookings.
When I click on an item, an invisible layout slides in from right to left, to display buttons.
I can dismiss this overlaying layout by clicking on a dismiss button so that it gets hidden again.
On this overlaying layout, I have a delete Button, which enables me to delete the item.
So far so good.
When I erase an item the item disappears as expected, the adapter is then reloaded.
The item below takes the position of the deleted item, but remains invisible.
I know it is here, because I can still click on the item to trigger the overlaying View.
So the ovelaying view is visible but not the item. I have no idea why this is happening.
I suspect the ViewHolder to be responsible of this behaviour, but I can't find a solution.
Thank you for your help.
See video here : http://youtu.be/KBGEvbUq-V0
My Bookings Class :
public class BookingsListFragment extends Fragment {
private final String SHOP_NAME_KEY = "ShopName";
private final String SHOP_ADDRESS_KEY = "ShopAddress";
public static int mSelectedItem = -1;
private static ListView mBookingsListView;
private static BookingsListViewAdapter mBookingsListViewAdapter;
private static ArrayList<Booking> mBookings;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(getActivity()));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bookings_list_fragment, container, false);
configureListView(view);
return view;
}
#Override
public void onResume() {
super.onResume();
mSelectedItem = -1;
}
private void configureListView(View view) {
mBookings = BookingsHandler.getBookings();
mBookingsListView = (ListView) view.findViewById(R.id.bookingsListView);
mBookingsListViewAdapter = new BookingsListViewAdapter();
mBookingsListView.setAdapter(mBookingsListViewAdapter);
mBookingsListView.setTextFilterEnabled(true);
}
public static void updateBookingsListView(ArrayList<Booking> mBookingsList){
mBookings = mBookingsList;
mBookingsListViewAdapter.notifyDataSetChanged();
}
static class ViewHolder {
LinearLayout bookingItemLL;
RelativeLayout optionsOverlay;
TextView productName;
TextView price;
TextView shopName;
TextView endDate;
ImageView productImage;
LinearLayout placeholderLL;
Button cancelBooking;
Button displayDirections;
Button callShop;
ImageView discardOverlay;
}
private class BookingsListViewAdapter extends BaseAdapter {
private static final int TYPE_ITEM = 0;
private static final int TYPE_PLACEHOLDER = 1;
#Override
public int getCount() {
if (mBookings != null)
return mBookings.size();
else
return 1;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
// Define a way to determine which layout to use
if (mBookings != null && mBookings.size() > 0)
return TYPE_ITEM;
else
return TYPE_PLACEHOLDER;
}
#Override
public int getViewTypeCount() {
return 2; // Number of different layouts
}
#Override
public View getView(final int position, View convertView, ViewGroup viewGroup) {
int type = getItemViewType(position);
final ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
switch (type){
case TYPE_ITEM :
convertView = LayoutInflater.from(getActivity()).inflate(R.layout.bookings_item, null);
holder.bookingItemLL = (LinearLayout) convertView.findViewById(R.id.bookingItemLL);
holder.optionsOverlay = (RelativeLayout) convertView.findViewById(R.id.bookingOptionsOverlay);
holder.productName = (TextView) convertView.findViewById(R.id.bookingProductName);
holder.price = (TextView) convertView.findViewById(R.id.bookedProductPrice);
holder.shopName = (TextView) convertView.findViewById(R.id.bookingShopName);
holder.endDate = (TextView) convertView.findViewById(R.id.bookingEndDate);
holder.productImage = (ImageView) convertView.findViewById(R.id.bookedProductImage);
holder.displayDirections = (Button) convertView.findViewById(R.id.routeShop);
holder.cancelBooking = (Button) convertView.findViewById(R.id.cancelBooking);
holder.callShop = (Button) convertView.findViewById(R.id.callShop);
holder.discardOverlay = (ImageView) convertView.findViewById(R.id.discardOverlay);
break;
case TYPE_PLACEHOLDER :
convertView = LayoutInflater.from(getActivity()).inflate(R.layout.booking_placeholder, null);
holder.placeholderLL = (LinearLayout) convertView.findViewById(R.id.placeHolderLL);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
if(type == 0) {
if(position == mSelectedItem){
holder.optionsOverlay.setVisibility(View.VISIBLE);
configureOverlayButtons(holder);
}
holder.bookingItemLL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mSelectedItem != position && mSelectedItem != -1){
View item = mBookingsListView.getChildAt(mSelectedItem - mBookingsListView.getFirstVisiblePosition());
if(item != null){
RelativeLayout overlayOptions = (RelativeLayout) item.findViewById(R.id.bookingOptionsOverlay);
overlayOptions.setVisibility(View.GONE);
}
}
Animation slideInAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.booking_options_overlay_animation);
holder.optionsOverlay.startAnimation(slideInAnimation);
holder.optionsOverlay.setVisibility(View.VISIBLE);
mSelectedItem = position;
configureOverlayButtons(holder);
}
});
final Booking booking = mBookings.get(position);
holder.productName.setText(booking.getName().toUpperCase());
holder.price.setText("Prix lors de la réservation : " + String.format("%.2f", Float.valueOf(booking.getPrice())) + " €");
holder.shopName.setText(booking.getShopName());
holder.endDate.setText(booking.getEndDate());
holder.productImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.product_placeholder)
.showImageOnFail(R.drawable.product_no_image_placeholder)
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(BeeWylApiClient.getImageUrl(booking.getImageURL()),holder.productImage, options);
}
if(type == 1){
holder.placeholderLL.setLayoutParams(BeeWylHelper.getPlaceHolderSizeForFreeScreenSpace(getActivity(),0));
}
return convertView;
}
private void configureOverlayButtons(final ViewHolder holder){
holder.cancelBooking.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder ab = new AlertDialog.Builder(getActivity());
ab.setMessage("Annuler la réservation ?").setPositiveButton("Oui", dialogClickListener)
.setNegativeButton("Non", dialogClickListener).show();
}
});
holder.displayDirections.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
launchMapActivity();
}
});
holder.callShop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
launchDialer();
}
});
holder.discardOverlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Animation hideOverlayAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.booking_overlay_dismiss);
holder.optionsOverlay.startAnimation(hideOverlayAnimation);
holder.optionsOverlay.setVisibility(View.GONE);
holder.optionsOverlay.clearAnimation();
}
});
}
private void sendCancelBookingToAPI(String id_booking) throws JsonProcessingException {
BeeWylApiClient.cancelBooking(id_booking, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int i, Header[] headers, byte[] bytes) {
try {
Log.v("xdebug CANCEL", new String(bytes, "UTF_8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) {
Log.v("xdebug CANCEL ERROR", String.valueOf(throwable));
}
});
}
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which){
case DialogInterface.BUTTON_POSITIVE:
Animation hideOverlayAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.booking_overlay_dismiss);
mBookingsListView.getChildAt(mSelectedItem-mBookingsListView.getFirstVisiblePosition()).startAnimation(hideOverlayAnimation);
new Handler().postDelayed(new Runnable() {
public void run() {
try {
sendCancelBookingToAPI(mBookings.get(mSelectedItem).getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
mBookings.remove(mSelectedItem);
mSelectedItem = -1;
updateBookingsListView(mBookings);
}
}, hideOverlayAnimation.getDuration());
break;
case DialogInterface.BUTTON_NEGATIVE:
dialog.cancel();
break;
}
}
};
}
}
And the item inflated :
<?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:paddingTop="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
>
<LinearLayout
android:id="#+id/bookingItemLL"
android:layout_width="match_parent"
android:layout_height="151dp"
android:orientation="horizontal"
android:weightSum="100"
android:background="#drawable/product_item_rectangle"
>
<ImageView
android:id="#+id/bookedProductImage"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#android:color/white"
android:src="#drawable/nivea"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical"
>
<TextView
android:id="#+id/bookingProductName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="BRUME NIVEA"
android:textColor="#color/ProductsBlue"
android:textSize="16dp"
android:textStyle="bold"
/>
<TextView
android:id="#+id/bookedProductPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Prix lors de la réservation : 24,90€"
android:textSize="12dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:textColor="#color/ProductsBlue" android:layout_gravity="left"
/>
<TextView
android:id="#+id/bookingShopName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:text="Magasin"
android:textSize="12dp"
android:textColor="#color/ProductsBlue"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:text="Réservé jusqu'au"
android:textSize="12dp"
android:textColor="#color/ProductsBlue" />
<TextView
android:id="#+id/bookingEndDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="-"
android:textSize="12dp"
android:textColor="#color/ProductsBlue" />
</LinearLayout>
</LinearLayout>
<RelativeLayout android:id="#+id/bookingOptionsOverlay"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#EEFFFFFF"
android:visibility="gone">
<ImageView
android:id="#+id/discardOverlay"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:src="#drawable/ic_discard_booking_overlay"
android:padding="5dp"
/>
<Button android:id="#+id/callShop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="APPELER"
android:layout_weight="1"
android:background="#00000000"
android:drawableTop="#drawable/booking_call"
android:textColor="#color/ProductsBlue"
android:textSize="14dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:drawablePadding="20dp"
android:layout_marginLeft="20dp"
/>
<Button android:id="#+id/cancelBooking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ANNULER"
android:layout_weight="1"
android:background="#00000000"
android:drawableTop="#drawable/booking_cancel"
android:textColor="#color/ProductsBlue"
android:textSize="14dp"
android:layout_centerInParent="true"
android:drawablePadding="20dp"
/>
<Button android:id="#+id/routeShop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ITINERAIRE"
android:layout_weight="1"
android:background="#00000000"
android:drawableTop="#drawable/booking_route"
android:textColor="#color/ProductsBlue"
android:textSize="14dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:drawablePadding="20dp"
android:layout_marginRight="20dp"
/>
</RelativeLayout>
</RelativeLayout>
Your problem comes from re-using the convertView.
When the previous item got a click the OnClickListener fired and in there the visibility for the item was set to GONE. Later on this same view got recycled and passed to getView() as the convertView. Because you are re-using it without resetting any changes that were made you are now working with a View for a new item that is not in a known state. You should make sure you undo any changes before using a convertView.
The quick fix is to not re-use the convertView that is passed into getView(). So, in your code where you check if you can re-use the convertView:
if(convertView == null)
Sabotage that check just to see if things start working:
if(true)
If that does the trick you will probably want to fix it properly.
In the else clause of the above check, you are getting the item holder from the tag. Also undo any changes that your OnClickListeners could have made. You want to start with a View for a new item in a known state. You should initialize it explicitly. For example:
if(convertView == null) {
// ... snipped all the initialization ...
} else {
holder = (ViewHolder)convertView.getTag();
convertView.setVisibility(View.VISIBLE);
}
Update
I have never used a 'heterogenous' adapter so I can't really answer why "the convertView is reusing the overlay View instead of my item's root View." The Android developer documentation for Adapter.getView() says about the convertView argument:
The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view. Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int)).
The emphasized bit says that you cannot depend on the system to pass you a convertView of the right type, while the last sentence says the opposite (as I read it).
Basically, I don't know why it's not working. I guess in the test where you check if you must inflate a new view yourself
if(convertView == null)
you should also check if it is the right kind of view:
if(convertView == null || getItemViewTypeFromView(convertView) != type)
Where getItemViewTypeFromView() is something like this:
private int getItemViewTypeFromView(View view) {
switch (view.getId()) {
case R.id.item_layout_root:
return TYPE_ITEM;
case R.id.placeholder_layout_root:
return TYPE_PLACEHOLDER;
default:
throw new UnsupportedOperationException();
}
}
In the item and placeholder layouts, give the root elements an id so you distinguish between them. So something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_layout_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp" >
... snipped the elements that make up the body of the layout ...
</RelativeLayout>
I haven't tried the above, so I hope it works for you.
Good luck!
I am using odoo mobile framework. My android app will connect to odoo server. I fetch the product from server and display in listview. I want to get multiple select value from listview. Here is my code. When I click a checkbox, another checkboxs also checked automatically. I am not sure it is due to OEListAdapter or my wrong code.
product_list.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/lvProductListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="?android:attr/actionBarSize" >
</ListView>
</LinearLayout>
product_list_row.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="5dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imgvProductPic"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_gravity="center_vertical"
android:src="#drawable/ic_launcher" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:padding="5dp" >
<TextView
android:id="#+id/txvProductName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Product Name"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/txvQty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Quantity"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="#+id/txvPrice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:text="Price"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<CheckBox
android:id="#+id/check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="4dip"
android:layout_marginRight="10dip"
android:focusable="false"
android:focusableInTouchMode="false" >
</CheckBox>
</LinearLayout>
Here is my activity code.
public class Products extends BaseFragment implements OETouchListener.OnPullListener,AdapterView.OnItemClickListener{
public static final String TAG = "com.odoo.addons.products.Products";
View mView = null;
ListView mListView = null;
OEListAdapter mListAdapter = null;
List<Object> mProductItems = new ArrayList<Object>();
OETouchListener mTouchAttacher;
ProductsLoader mProductsLoader = null;
ArrayList<Integer> checkedPositions = new ArrayList<Integer>();
CheckBox ckProduct;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setHasOptionsMenu(true);
mView = inflater.inflate(R.layout.products_list_layout, container, false);
scope = new AppScope(getActivity());
init();
return mView;
}
/**
* Map View and Product Items
*/
private void init() {
Log.d(TAG, "call init()");
mListView = (ListView) mView.findViewById(R.id.lvProductListView);
mListAdapter = new OEListAdapter(getActivity(),R.layout.products_list_row, mProductItems) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View mView = convertView;
final int holder = position;
if (mView == null)
mView = getActivity().getLayoutInflater().inflate(getResource(), parent, false);
Log.d(TAG,"This is "+position+".");
OEDataRow row = (OEDataRow) mProductItems.get(position);
ImageView imgProduct = (ImageView) mView.findViewById(R.id.imgvProductPic);
Bitmap bitmap = null;
if(row != null){
String base64Image = row.getString("image_small");
bitmap = Base64Helper.getBitmapImage(getActivity(), base64Image);
}
imgProduct.setImageBitmap(bitmap);
TextView txvProductName, txvQty, txvPrice;
ckProduct = (CheckBox) mView.findViewById(R.id.check);
txvProductName = (TextView) mView.findViewById(R.id.txvProductName);
txvQty = (TextView) mView.findViewById(R.id.txvQty);
txvPrice = (TextView) mView.findViewById(R.id.txvPrice);
txvProductName.setText(row.getString("name"));
txvQty.setText(row.getString("qty_available"));
txvPrice.setText(row.getString("lst_price"));
// when checkbox is clicked, we add/remove its position to/from the list
ckProduct.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (((CheckBox) v).isChecked()) {
// if checked, we add it to the list
checkedPositions.add(holder);
}
else if(checkedPositions.contains(holder)) {
// else if remove it from the list (if it is present)
checkedPositions.remove(holder);
}
}
});
// set the state of the checbox based on if it is checked or not.
ckProduct.setChecked(checkedPositions.contains(holder));
return mView;
}
};
mTouchAttacher = scope.main().getTouchAttacher();
mTouchAttacher.setPullableView(mListView, this);
// mListView.setOnItemClickListener(this);
mProductsLoader = new ProductsLoader();
mProductsLoader.execute();
}
// View.OnItemClickListener productCheckboxClickListener = new View.OnClickListener() {
// #Override
// public void onClick(View v) {
// Toast.makeText(getActivity(),"got",Toast.LENGTH_SHORT).show();
// }
// };
// Call ProductDB aka ORM Object
#Override
public Object databaseHelper(Context context) {
return new ProductsDB(context);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Log.d(TAG,"call onCreateOptionMenu");
inflater.inflate(R.menu.menu_fragment_products, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.d(TAG,"call onOptionsItemSelected");
switch (item.getItemId()) {
case R.id.menu_product_next:
Toast.makeText(getActivity(),"Test"+checkedPositions+"test",Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// Add Menu Drawer Item
#Override
public List<DrawerItem> drawerMenus(Context context) {
List<DrawerItem> menu = new ArrayList<DrawerItem>();
menu.add(new DrawerItem(TAG, "Products", true));
menu.add(new DrawerItem(TAG, "Products", 0, R.drawable.ic_action_todo,getFragment("Products")));
return menu;
}
// Define Fragment
private Fragment getFragment(String value) {
Products products = new Products();
Bundle bundle = new Bundle();
bundle.putString("name", value);
products.setArguments(bundle);
return products;
}
/**
* Loading product list from another thread
*/
class ProductsLoader extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void ...params){
Log.d(TAG,"Call ProductsLoader()");
mProductItems.clear();
ProductsDB db = new ProductsDB(getActivity());
mProductItems.addAll(db.select());
return true;
}
#Override
protected void onPostExecute(Boolean success) {
Log.d(TAG,"Call onPostExecute");
checkProducts();
}
}
/**
* Check empty database or not
* TODO Need to fix check status
*/
private void checkProducts() {
if(mProductItems.size() != 0){
mListAdapter.notifiyDataChange(mProductItems);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mListView.setAdapter(mListAdapter);
mListView.setOnItemClickListener(this);
}
}
/**
* On Product item click
*/
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position,
long id) {
OEDataRow row = (OEDataRow) mProductItems.get(position);
Toast.makeText(getActivity(),row.getString("id"),Toast.LENGTH_SHORT).show();
}
/**
* Watch Sync status and inform ProductsLoader
*/
#Override
public void onResume() {
super.onResume();
getActivity().registerReceiver(mSyncFinish, new IntentFilter(SyncFinishReceiver.SYNC_FINISH));
}
#Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mSyncFinish);
}
SyncFinishReceiver mSyncFinish = new SyncFinishReceiver() {
public void onReceive(Context context, android.content.Intent intent) {
mTouchAttacher.setPullComplete();
mProductsLoader = new ProductsLoader();
mProductsLoader.execute();
}
};
/**
* on pulled for sync message
*/
#Override
public void onPullStarted(View arg0) {
scope.main().requestSync(ProductsProvider.AUTHORITY);
}
}
I know this is too late for this question , but for handling checkbox inside a listview you should use viewholder pattern ,
create a arraylist of boolean that will store your selection here is sample code
Declaration of arraylist
public static ArrayList<Boolean> arrChecked;
initialise the list
for (int i = 0; i < data.size(); i++) {
arrChecked.add(false);
}
then in getView method use holder like this ,
if (convertView == null) {
convertView = inflater.inflate(R.layout.listview_item, null);
ViewHolder viewHolder = new ViewHolder();
viewHolder.cb = (CheckBox) convertView.findViewById(R.id.checkbox);
// The tag can be any Object, this just happens to be the ViewHolder
convertView.setTag(viewHolder);
}
holder = (ViewHolder) convertView.getTag();
// set position as id
holder.cb.setId(position);
holder.cb.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int id = v.getId();
if (arrChecked.get(id)) {
arrChecked.set(id, false);
// unchecked state of checkbox
} else {
arrChecked.set(id, true);
//do what you want to do in checked state here
}
}
});
holder.cb.setChecked(arrChecked.get(position));
I would like to get a click anywhere on the row of my ListActivity to proceed to another Activity. Currently, i am able to see the row position in a log when the ImageView is clicked, but the click is not registered anywhere else in the row. Furthermore, the Toast command crashes the program. Any assistance toward my stated goal would be much appreciated. Thanks!
event_row.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="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:contentDescription="#string/event_icon_desc"
android:src="#drawable/event_icon" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="#+id/event_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:textIsSelectable="true" />
<TextView
android:id="#+id/location_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:textIsSelectable="true" />
<TextView
android:id="#+id/date_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:textIsSelectable="true" />
</LinearLayout>
</LinearLayout>
display_events_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/event_ll" >
<ListView
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<TextView
android:id="#+id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="#string/tv_empty" />
</LinearLayout>
DisplayEvents.java:
public class DisplayEvents extends ListActivity {
private ProgressDialog progressDialog = null;
private ArrayList<Event> events = new ArrayList<Event>();
private EventAdapter eventAdapter;
private Runnable viewEvents;
//private final int ITEMS_PER_VIEW = 10;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.disp_events_activity);
new EventTask().execute();
this.eventAdapter = new EventAdapter(this, R.layout.event_row, this.events);
setListAdapter(this.eventAdapter);
viewEvents = new Runnable() {
#Override
public void run() {
getEvents();
}
};
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
private void getEvents() {
runOnUiThread(adaptEvents);
}
private Runnable adaptEvents = new Runnable() {
#Override
public void run() {
if (events != null && events.size() > 0) {
eventAdapter.notifyDataSetChanged();
for (int i=0; i<events.size(); i++)
eventAdapter.add(events.get(i));
}
progressDialog.dismiss();
eventAdapter.notifyDataSetChanged();
}
};
private class EventAdapter extends ArrayAdapter<Event> {
private ArrayList<Event> events;
public EventAdapter(Context context, int textViewResourceId, ArrayList<Event> events) {
super(context, textViewResourceId, events);
this.events = events;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.event_row, null);
}
Event event = events.get(position);
if (event != null) {
TextView event_tv = (TextView) v.findViewById(R.id.event_tv);
TextView loc_tv = (TextView) v.findViewById(R.id.location_tv);
TextView date_tv = (TextView) v.findViewById(R.id.date_tv);
if (event_tv != null)
event_tv.setText("Event: " + event.getEventName());
if (loc_tv != null)
loc_tv.setText("Location: " + event.getPlace() + " - " + event.getCity());
if (date_tv != null)
date_tv.setText("Date: " + event.getDate());
}
v.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
Log.v("text", "Image clicked, row %d"+position);
Toast.makeText(DisplayEvents.this, position, Toast.LENGTH_LONG).show();
}
});
return v;
}
}
public class EventTask extends AsyncTask<Void, Void, ArrayList<Event>> {
#Override
protected ArrayList<Event> doInBackground(Void... Void) {
ArrayList<Event> dbEvents = new ArrayList<Event>();
BuyTicketsConnection buyConnection = new BuyTicketsConnection();
dbEvents = buyConnection.getEventsList();
return dbEvents;
}
protected void onPostExecute(ArrayList<Event> dbEvents) {
events = dbEvents;
Thread thread = new Thread(null, viewEvents, "GetEventsBackground");
thread.start();
progressDialog = ProgressDialog.show(DisplayEvents.this, "Please wait...", "Retrieving events...", true);
}
}
}
EDIT:
I've now got this method:
#Override
protected void onListItemClick (ListView l, View v, int position, long id){
Log.v("text", "Image clicked, row %d"+position);
Toast.makeText(DisplayEvents.this, position, Toast.LENGTH_LONG).show();
}
in my DisplayEvents (ListActivity) class, but still no dice. Also, i'd like the click to respond to anything in that list item (note there are many views in that row layout which should inherit the click event handler).
it seems that the issue is how i should get all of the views (ImageView and 3 TextViews) in event_row.xml to get associated with this OnListItemClick handler. how is this entire row (comprised of 4 views in 2 LinearLayouts) recognized in the context of that OnListItemClick handler?
Use setOnItemClickListener on your DisplayEvents Activity and remove setOnClickListener from your EventAdapter.
The problem is that some of your item views are focusable and/or clickable (thus they eat the click events and that causes the ListView to not be able to receive the event in order to know that you clicked on a list item). For a list to trigger item-click events, it must be able to receive the touch events without them being eaten by the children.
In your item layout, you have 3 TextViews that all have textIsSelectable=true, and that means the TextView itself will eat the touch events. You need to disable that in order to trigger the onListItemClick() events.
I encountered this problem and removing
android:textIsSelectable="true"
will solve it.
From what I can gather it appears that this might be because my ListView is not being displayed, I've verified that getCount is returning a value not zero, but I can't see what I'm doing wrong.
Everything loads and acts like it's working but the ListView never appears, I put a background color on the fragment reference in mixed.xml and it is there and taking up the full screen, but when I set a background color on my ListView it does not appear, it's like it's not being rendered at all.
More odd, getView is not being called in my adapter, and this is all working code from regular activities that I ported to fragments.
I've tried calling notifyDataSetChanged which didn't changed anything, debugging shows the adapter is filled with data and getCount is indeed returning an accurate count greater than 0.
Thanks for any help, I'm stuck.
Project is open and can be viewed here http://code.google.com/p/shack-droid/source/browse/#svn%2FTrunk but I'm also including the pertinent code here.
This is the ListFragment:
public class FragmentTopicView extends ListFragment implements ShackGestureEvent {
private ArrayList<ShackPost> posts;
private String storyID = null;
private String errorText = "";
private Integer currentPage = 1;
private Integer storyPages = 1;
private String loadStoryID = null;
private Boolean threadLoaded = true;
private Hashtable<String, String> postCounts = null;
private AdapterLimerifficTopic tva;
public FragmentTopicView() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.topics, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final ShackGestureListener listener = Helper.setGestureEnabledContentView(R.layout.topics, getActivity());
if (listener != null) {
listener.addListener(this);
}
if (savedInstanceState == null) {
// get the list of topics
GetChattyAsyncTask chatty = new GetChattyAsyncTask(getActivity());
chatty.execute();
}
ListView lv = getListView();
lv.setOnScrollListener(new OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// start loading the next page
if (threadLoaded && firstVisibleItem + visibleItemCount >= totalItemCount && currentPage + 1 <= storyPages) {
// get the list of topics
currentPage++;
GetChattyAsyncTask chatty = new GetChattyAsyncTask(getActivity());
chatty.execute();
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
});
}
class GetChattyAsyncTask extends AsyncTask<String, Void, Void> {
protected ProgressDialog dialog;
protected Context c;
public GetChattyAsyncTask(Context context) {
this.c = context;
}
#Override
protected Void doInBackground(String... params) {
threadLoaded = false;
try {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);
final String feedURL = prefs.getString("shackFeedURL", getString(R.string.default_api));
final URL url;
if (loadStoryID != null) {
if (currentPage > 1)
url = new URL(feedURL + "/" + loadStoryID + "." + currentPage.toString() + ".xml");
else
url = new URL(feedURL + "/" + loadStoryID + ".xml");
}
else {
if (currentPage > 1)
url = new URL(feedURL + "/" + storyID + "." + currentPage.toString() + ".xml");
else
url = new URL(feedURL + "/index.xml");
}
// Get a SAXParser from the SAXPArserFactory.
final SAXParserFactory spf = SAXParserFactory.newInstance();
final SAXParser sp = spf.newSAXParser();
// Get the XMLReader of the SAXParser we created.
final XMLReader xr = sp.getXMLReader();
// Create a new ContentHandler and apply it to the XML-Reader
SaxHandlerTopicView saxHandler = new SaxHandlerTopicView(c, "topic");
xr.setContentHandler(saxHandler);
// Parse the xml-data from our URL.
xr.parse(new InputSource(HttpHelper.HttpRequestWithGzip(url.toString(), c)));
// Our ExampleHandler now provides the parsed data to us.
if (posts == null) {
posts = saxHandler.GetParsedPosts();
}
else {
ArrayList<ShackPost> newPosts = saxHandler.GetParsedPosts();
newPosts.removeAll(posts);
posts.addAll(posts.size(), newPosts);
}
storyID = saxHandler.getStoryID();
storyPages = saxHandler.getStoryPageCount();
if (storyPages == 0) // XML returns a 0 for stories with only
// one page
storyPages = 1;
}
catch (Exception ex) {
// TODO: implement error handling
}
return null;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
if (currentPage == 1)
dialog = ProgressDialog.show(getActivity(), null, "Loading Chatty", true, true);
else
SetLoaderVisibility(View.VISIBLE);
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
ShowData();
SetLoaderVisibility(View.GONE);
try {
dialog.dismiss();
}
catch (Exception e) {
}
}
}
private void ShowData() {
if (posts != null) {
Hashtable<String, String> tempHash = null;
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
final String login = prefs.getString("shackLogin", "");
final int fontSize = Integer.parseInt(prefs.getString("fontSize", "12"));
try {
postCounts = GetPostCache();
}
catch (Exception ex) {
}
if (postCounts != null)
tempHash = new Hashtable<String, String>(postCounts);
if (tva == null) {
tva = new AdapterLimerifficTopic(getActivity(), R.layout.lime_topic_row, posts, login, fontSize, tempHash);
setListAdapter(tva);
}
else {
tva.SetPosts(posts);
tva.notifyDataSetChanged();
}
final ListView lv = getListView();
lv.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.setHeaderTitle("Options");
menu.add(0, 1, 0, "Copy Post Url to Clipboard");
menu.add(0, 2, 0, "Watch Thread");
menu.add(0, 3, 0, "Thread Expires In?");
menu.add(0, 4, 0, "Shacker's Chatty Profile");
}
});
// update the reply counts for the listing of topics
try {
UpdatePostCache();
}
catch (Exception e) {
}
}
else {
if (errorText.length() > 0) {
try {
new AlertDialog.Builder(getActivity()).setTitle("Error").setPositiveButton("OK", null).setMessage(errorText).show();
}
catch (Exception ex) {
// could not create a alert for the error for one reason
// or another
Log.e("ShackDroid", "Unable to create error alert ActivityTopicView:468");
}
}
}
threadLoaded = true;
}
}
Here's my FragmentActivity:
public class FragmentActivityTopic extends FragmentActivity {
#Override
protected void onCreate(Bundle arg) {
// TODO Auto-generated method stub
super.onCreate(arg);
setContentView(R.layout.mixed);
}
}
This is the Adapter I'm using, and as mentioned above getView is not being called:
public class AdapterLimerifficTopic extends BaseAdapter {
// private Context context;
private List<ShackPost> topicList;
private final int rowResouceID;
private final String shackLogin;
private final Typeface face;
private final int fontSize;
private final Hashtable<String, String> postCache;
private final String showAuthor;
private final Resources r;
private int totalNewPosts = 0;
LayoutInflater inflate;// = LayoutInflater.from(context);
public AdapterLimerifficTopic(Context context, int rowResouceID, List<ShackPost> topicList, String shackLogin, int fontSize, Hashtable<String, String> postCache) {
this.topicList = topicList;
this.rowResouceID = rowResouceID;
this.shackLogin = shackLogin;
this.fontSize = fontSize;
this.postCache = postCache;
this.r = context.getResources();
face = Typeface.createFromAsset(context.getAssets(), "fonts/arial.ttf");
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
showAuthor = prefs.getString("showAuthor", "count");
inflate = LayoutInflater.from(context);
}
public void SetPosts(List<ShackPost> posts)
{
topicList = posts;
}
#Override
public int getCount() {
return topicList.size();
}
#Override
public Object getItem(int position) {
return topicList.get(position);
}
#Override
public long getItemId(int position) {
// return position;
final ShackPost post = topicList.get(position);
return Long.parseLong(post.getPostID());
}
static class ViewHolder {
TextView posterName;
TextView datePosted;
TextView replyCount;
TextView newPosts;
TextView postText;
TextView viewCat;
RelativeLayout topicRow;
ImageView postTimer;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TextView tmp;
// final View v;
ViewHolder holder;
final ShackPost post = topicList.get(position);
if (convertView == null) {
convertView = inflate.inflate(rowResouceID, parent, false);
holder = new ViewHolder();
holder.posterName = (TextView) convertView.findViewById(R.id.TextViewLimeAuthor);
holder.datePosted = (TextView) convertView.findViewById(R.id.TextViewLimePostDate);
holder.replyCount = (TextView) convertView.findViewById(R.id.TextViewLimePosts);
holder.newPosts = (TextView) convertView.findViewById(R.id.TextViewLimeNewPosts);
holder.postText = (TextView) convertView.findViewById(R.id.TextViewLimePostText);
holder.viewCat = (TextView) convertView.findViewById(R.id.TextViewLimeModTag);
// holder.topicRow = (RelativeLayout) convertView.findViewById(R.id.TopicRow);
// holder.postTimer = (ImageView) convertView.findViewById(R.id.ImageViewTopicTimer);
// holder.posterName.setTypeface(face);
// holder.datePosted.setTypeface(face);
// holder.replyCount.setTypeface(face);
// holder.newPosts.setTypeface(face);
holder.postText.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
// holder.postText.setTypeface(face);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
// holder.postTimer.setImageResource(Helper.GetTimeLeftDrawable(post.getPostDate()));
//
holder.posterName.setText(post.getPosterName());
//
// if (shackLogin.equalsIgnoreCase(post.getPosterName()))
// holder.posterName.setTextColor(Color.parseColor("#00BFF3"));
// else
// holder.posterName.setTextColor(Color.parseColor("#ffba00"));
//
holder.datePosted.setText(Helper.FormatShackDateToTimePassed(post.getPostDate()));
holder.replyCount.setText(post.getReplyCount());
//
// if (showAuthor.equalsIgnoreCase("count") && post.getIsAuthorInThread())
// holder.replyCount.setTextColor(Color.parseColor("#0099CC"));
// else
// holder.replyCount.setTextColor(Color.parseColor("#FFFFFF"));
// clipped code
holder.postText.setText(preview);
// if (showAuthor.equalsIgnoreCase("topic") && post.getIsAuthorInThread()) {
// final Drawable d = r.getDrawable(R.drawable.background_gradient_blue);
// holder.topicRow.setBackgroundDrawable(d);
// }
// else
// holder.topicRow.setBackgroundDrawable(null);
// TODO: clean this up a little / also replicated in ShackDroidThread ick
final String postCat = post.getPostCategory();
holder.viewCat.setVisibility(View.VISIBLE);
if (postCat.equals("offtopic")) {
holder.viewCat.setText("offtopic");
holder.viewCat.setBackgroundColor(Color.parseColor("#444444"));
}
else if (postCat.equals("nws")) {
holder.viewCat.setText("nws");
holder.viewCat.setBackgroundColor(Color.parseColor("#CC0000"));
}
else if (postCat.equals("political")) {
holder.viewCat.setText("political");
holder.viewCat.setBackgroundColor(Color.parseColor("#FF8800"));
}
else if (postCat.equals("stupid")) {
holder.viewCat.setText("stupid");
holder.viewCat.setBackgroundColor(Color.parseColor("#669900"));
}
else if (postCat.equals("informative")) {
holder.viewCat.setText("interesting");
holder.viewCat.setBackgroundColor(Color.parseColor("#0099CC"));
}
else
holder.viewCat.setVisibility(View.GONE);
return convertView;
}
public int getTotalNewPosts() {
return totalNewPosts;
}
}
And related XML:
mixed.xml (this is the layout for the fragments, only the one for now)
<?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:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:id="#+id/LinearLayoutMixed">
<fragment
android:id="#+id/MixedThreads"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
class="com.stonedonkey.shackdroid.FragmentTopicView"
>
</fragment>
</LinearLayout>
Topics.xml (this contains the ListView as well as a sliding tray and some other stuff.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/TopicLoader"
android:divider="#333333"
android:dividerHeight="1dip"
android:textColor="#FFFFFF"
/>
<RelativeLayout
android:id="#+id/TopicLoader"
android:layout_width="fill_parent"
android:layout_height="35dip"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal"
android:visibility="gone"
android:layout_marginTop="5dip" >
<TextView
android:id="#+id/TextViewTopicLoaderText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false"
android:text="Loading"
>
</TextView>
<ImageView
android:id="#+id/ImageViewTopicLoader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/TextViewTopicLoaderText"
android:src="#drawable/ic_action_refresh"
android:layout_alignBottom="#+id/TextViewTopicLoaderText"
/>
</RelativeLayout>
<SlidingDrawer
android:id="#+id/SlidingDrawer01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/TopicLoader"
android:animateOnClick="true"
android:content="#+id/bookMarkParent"
android:handle="#+id/TextViewTrayHandle"
android:orientation="vertical"
android:paddingTop="200dip"
android:visibility="gone" >
<TextView
android:id="#+id/TextViewTrayHandle"
android:layout_width="fill_parent"
android:layout_height="35dip"
android:background="#drawable/darkgrey_gradient"
android:focusable="false"
android:focusableInTouchMode="false"
android:gravity="center_vertical" >
</TextView>
<RelativeLayout
android:id="#id/bookMarkParent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ListView
android:id="#+id/ListViewWatchedThreads"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:divider="#333333"
android:dividerHeight="1dip"
android:textColor="#FFFFFF" >
</ListView>
</RelativeLayout>
</SlidingDrawer>
</RelativeLayout>
and finally lime_topic_row which is my custom row layout for the ListView in the above layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FF0000" >
<TextView
android:id="#+id/TextViewLimeModTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FF0000"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dip"
android:textColor="#000000"
android:padding="2dip"
android:textSize="10dip"
/>
<TextView
android:id="#+id/TextViewLimePostText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="20dip"
android:padding="10dip"
android:layout_below="#+id/TextViewLimeModTag"
android:textColor="#FFFFFF" />
<TextView
android:id="#+id/TextViewLimeAuthor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/TextViewLimePostText"
android:paddingBottom="10dip"
android:paddingLeft="10dip"
android:textColor="#0099CC" />
<TextView
android:id="#+id/TextViewPosted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/TextViewLimePostText"
android:layout_toRightOf="#+id/TextViewLimeAuthor"
android:paddingBottom="10dip"
android:text=" posted " />
<TextView
android:id="#+id/TextViewLimePostDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/TextViewLimePostText"
android:layout_toRightOf="#+id/TextViewPosted"
android:paddingBottom="10dip"
android:paddingRight="10dip"
android:textColor="#FF8800" />
<TextView
android:id="#+id/TextViewLimePosts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/TextViewLimePostDate"
android:layout_marginRight="3dip"
android:layout_below="#+id/TextViewLimePostText"
android:layout_marginBottom="15dip"
android:layout_toLeftOf="#+id/TextViewLimeNewPosts"
android:background="#BBBBBB"
android:padding="3dip"
android:minWidth="25dip"
android:gravity="center"
android:textColor="#000000" />
<TextView
android:id="#+id/TextViewLimeNewPosts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/TextViewLimePostDate"
android:layout_below="#+id/TextViewLimePostText"
android:layout_marginBottom="15dip"
android:layout_marginRight="10dip"
android:background="#669900"
android:padding="3dip"
android:minWidth="25dip"
android:gravity="center"
android:layout_alignParentRight="true"
android:textColor="#000000" />
</RelativeLayout>
I think I found the problem. The issue appears because of the ShackGestureListener that you setup at the start of the onActivityCreated method in the FragmentTopicView:
final ShackGestureListener listener = Helper.setGestureEnabledContentView(R.layout.topics, getActivity());
if (listener != null) {
listener.addListener(this);
}
In the setGestureEnabledContentView() method you check to see if the user enabled the gestures in the preferences or if the android version is bigger then 3. Either way, true or false you set the content view for the FragmentActivityTopic again(with the layout of the FragmentTopicView). Setting the content view again will, unfortunately, cover the current layout which holds the ListView with data(ListView that populates with no problems). When you run those AsyncTasks to get the data, at the end you set the data on the correct ListView(returned by getListView) because the getListView will hold a reference to the old correct ListView which was set in the onCreateView method, but you don't see anything because in the setGestureEnabledContentView you cover this ListView.
This behavior is easy to see if you simple comment out(or remove) the lines that set the content view for the activity in the Helper and HelperAPI4 classes. Another way to see that your ListView is covered is, for example to set the adapter for the ListView(tva) using getListView and using the getActivity().findViewById(android.R.id.list)(I've done this on selecting one of your menus items, so I can control when I replace the adapter):
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent intent;
switch (item.getItemId())
{
case R.id.topic_menu_newpost: // Launch post form
//this doesn't work as the getListView will return a reference to the old ListView
ListView lv = getListView();
lv.setAdapter(tva);
// this will work as you get a reference to the new ListView on top
ListView lv = (ListView) getActivity().findViewById(android.R.id.list);
lv.setAdapter(tva);
I don't know what to recommend as a solution as I don't quite understand what you're doing, but I doubt that it requires to set the content view for the activity again(and you should work from this).