I've created a custom RelativeLayout view which I inflate with merge tags.
In this custom view I have a button which I want it to do something.
I've tried many things but the button simply refuses to be clicked.
The strange part is, I can find the views and change their visibility just fine.
Is it possible to click a button this way, or should it be done in a different way?
The things I've tried:
Anonymous innerclass for onClickListener
XML attribute for onClick
View onClick with onClickListener(this)
Check if it's clickable with code (it returns true)
Added clickable(true) to both XML and code.
Private context from constructor instead of getContext()
Moved logic from init() to onFinishInflate()
Used the view from inflater to find views
Switch from inflate() to LayoutInflater
Inflated custom view:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/internet_view_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/internet_offline"/>
<custom.IconView
android:id="#+id/internet_view_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="ICON"
android:visibility="invisible"
android:layout_below="#+id/internet_view_text"/>
<Button
android:id="#+id/internet_view_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="BUTTON"
android:clickable="true"
android:layout_below="#+id/internet_view_text"/>
</merge>
Relevant part of the parent view:
<custom.NoInternetView
android:id="#+id/webview_no_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
app:automaticReconnect="false"/>
The class file:
public class NoInternetView extends RelativeLayout implements View.OnClickListener {
private static final String TAG = NoInternetView.class.getSimpleName();
private static ConnectivityChangeListener listener;
private static boolean automaticReconnect;
private Context mContext;
private View view;
private Button button;
public NoInternetView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NoInternetView, defStyleAttr, 0);
automaticReconnect = a.getBoolean(R.styleable.NoInternetView_automaticReconnect, false);
a.recycle();
init();
}
public NoInternetView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NoInternetView(Context context) {
this(context, null);
}
#Override
protected void onFinishInflate() {
Log.d(TAG, "onFinishInflate");
super.onFinishInflate();
button = (Button) view.findViewById(R.id.internet_view_button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Clicked on some Button");
if (listener != null && NetworkHelper.hasAccess(getContext())) {
listener.connected();
}
}
});
button.setClickable(true);
boolean clicked = button.callOnClick();
Log.d(TAG, "clicked: "+clicked);
TextView text = (TextView) view.findViewById(R.id.internet_view_icon);
text.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Clicked on some TextView");
if (listener != null && NetworkHelper.hasAccess(getContext())) {
listener.connected();
}
}
});
//check if the attribute 'automaticReconnect' is set to true
//if so, show an icon instead of a button
if (automaticReconnect){
button.setVisibility(INVISIBLE);
view.findViewById(R.id.internet_view_icon).setVisibility(VISIBLE);
}
}
public void init(){
Log.d(TAG, "init");
view = LayoutInflater.from(mContext).inflate(R.layout.no_internet_view, this, false);
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d(TAG, "something happened?");
return super.dispatchKeyEvent(event);
}
#Override
public void onClick(View v) {
Log.d(TAG, "Clicked on Button(TextView)");
if (listener != null && NetworkHelper.hasAccess(getContext())){
listener.connected();
}
}
So I got it to work. Here are the things I did to make it work.
Replace the merge tag with RelativeLayout, because you are inflating the layout to show inside a view. More info here: Android xml merge layout error on inflate
You have set android:visibility="invisible", change that to android:visibility="visible" (This is just to get the view visible, you can later change it programatically)
Add the below line to the init() method of your NoInternetView. You are inflating the layout with all the views, but only when you add this view, you will be able to see it.
public void init(){
Log.d(TAG, "init");
view = LayoutInflater.from(mContext).inflate(R.layout.no_internet_view, this, false);
this.addView(view);}
Once you are done with this, the button will be visible and clicking on the button will be clickable. You can either use anonymous onClickListener or make the view implement the onClickListener and override the onClick() method. Anything works.
Related
I have a gridview whose item layout implements checkable, thus the gridview can handle checking a checkbox within the item layout when an item is selected. This all works fine except for setting a gridview item selected programatically. The check box doesn't look selected but something must be happening in the background as selecting the item after that leaves it unchecked and upon selecting again it becomes checked.
Any ideas?
Edit: It seems that the staying unselected on click was due to some other logic in my code so that could be a red herring to the actual issue.
Checkable layout
public class WeedFilterItem extends LinearLayout implements Checkable {
private TextView label;
private CheckBox checkBox;
private boolean mChecked;
public WeedFilterItem(Context context) {
super(context);
init();
}
public WeedFilterItem(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public WeedFilterItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
inflate(getContext(), R.layout.weed_filter_item, this);
this.label = (TextView) findViewById(R.id.filter_textview);
this.checkBox = (CheckBox) findViewById(R.id.checkbox);
}
public void setChecked(boolean checked) {
mChecked = checked;
this.checkBox.setChecked(checked);
}
public boolean isChecked() {
return mChecked;
}
public void toggle() {
setChecked(!mChecked);
}
}
item layout xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="#+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"
android:layout_gravity="center"
android:scaleX="1.5"
android:scaleY="1.5"/>
<TextView
android:id="#+id/filter_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:layout_gravity="center"
android:paddingTop="10dp"
/>
</LinearLayout>
Setting up gridview
final GridView filters = (GridView) child.findViewById(R.id.filter_gridview);
final WeedFilterGridViewAdapter adapter = new WeedFilterGridViewAdapter(this, values);
filters.setAdapter(adapter);
filters.setSelection(0);
Everything else is handled by a stock gridview, just trying to call gridView.setSelection(int) on it.
I've also tried storing the int for the selected item in the adapter and setting the checkbox selected manually in getView along with calling notifyDataSetChanged() but that also doesn't work.
UPDATED
final GridView filters = (GridView) child.findViewById(R.id.filter_gridview);
final WeedFilterGridViewAdapter adapter = new WeedFilterGridViewAdapter(this, values);
filters.setAdapter(adapter);
filters.setSelection(0);
// add this line
filters.setItemChecked(0, true);
http://developer.android.com/reference/android/widget/AbsListView.html#setItemChecked(int, boolean)
public void setItemChecked (int position, boolean value)
Added in API level 1 Sets the checked state of the specified position.
The is only valid if the choice mode has been set to
CHOICE_MODE_SINGLE or CHOICE_MODE_MULTIPLE.
Parameters position The item whose checked state is to be checked
value The new checked state for the item
I am new in this stuff so i hope its not a ridiculous question...
I have a list view, and all the items including a video, numOfLikes(textview), like and dislike (Buttons).
When I click on the like or dislike button, I try to change the background of the button, but it changes the background of all the like/dislike buttons in the list view.
The same happens when I am trying to enable the dislike button while I am clicking on the like button, it disables all the dislike buttons in the list.
Code :
public class FeedAdapter extends ArrayAdapter<Feed> {
Context context;
ArrayList<Feed> feedsList;
ArrayList<String> listOfItems;
Dialog dialog;
public FeedAdapter(Context context, int resource, ArrayList<Feed> feeds) {
super(context, resource, feeds);
this.context = context;
this.feedsList = feeds;
}
public FeedAdapter(Context context, ArrayList<Feed> feeds){
super(context, R.layout.feed_listitem, feeds);
this.context = context;
this.feedsList = feeds;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final FeedHolder holder;
if(convertView == null){
convertView = LayoutInflater.from(context).inflate(R.layout.feed_listitem, parent, false);
holder = new FeedHolder();
holder.titleTextView = (TextView) convertView.findViewById(R.id.feed_title_textView);
holder.usernameTextView = (TextView) convertView.findViewById(R.id.feed_name_textview);
holder.likesTextView = (TextView) convertView.findViewById(R.id.feed_likes_textview);
holder.likeButton = (Button) convertView.findViewById(R.id.feed_like_button);
holder.unlikeButton = (Button) convertView.findViewById(R.id.feed_unlike_button);
holder.video = (VideoView) convertView.findViewById(R.id.feed_post_videoView);
holder.frameLayout = (FrameLayout) convertView.findViewById(R.id.feed_placeholder_framelayout);
holder.frameLayout.setTag(holder.video);
holder.likeButton.setTag(holder.unlikeButton);
holder.unlikeButton.setTag(holder.likeButton);
convertView.setTag(holder);
} else{
holder = (FeedHolder) convertView.getTag();
holder.frameLayout.setTag(holder.video);
holder.likeButton.setTag(holder.unlikeButton);
holder.unlikeButton.setTag(holder.likeButton);
}
holder.titleTextView.setText(feedsList.get(position).getTitle());
holder.usernameTextView.setText(feedsList.get(position).getUsername());
holder.likesTextView.setText(TrendliContract.showNumInNumK(feedsList.get(position).getLikesInLong()));
holder.titleTextView.setTypeface(TrendliContract.helvetica);
holder.usernameTextView.setTypeface(TrendliContract.helvetica);
holder.likesTextView.setTypeface(TrendliContract.helvetica);
holder.frameLayout.setBackground(feedsList.get(position).getDrawable());
holder.video.setVisibility(View.INVISIBLE);
holder.video.setMediaController(new MediaController(context));
holder.video.setVideoURI(Uri.parse(feedsList.get(position).getVideoImageGif()));
holder.video.setLayoutParams(new FrameLayout.LayoutParams(TrendliContract.screenW,
TrendliContract.screenW));
postImageViewOnClickListener(holder);
holder.likeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Feed f = feedsList.get(position);
//Button b = ((Button) v.getTag());
if(f.isLikePressed() == false){
new TrendliContract.Like().execute(f);
f.setLikePressed(true);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().
toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(++numOfLikes));
//v.setBackgroundColor(R.color.black);
//b.setEnabled(false);
} else{
new TrendliContract.UnLike().execute(f);
f.setLikePressed(false);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().
toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(--numOfLikes));
//v.setBackgroundColor(R.color.transparent);
//b.setEnabled(true);
}
}
});
holder.unlikeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Feed f = feedsList.get(position);
//Button b = ((Button) v.getTag());
if(f.isDislikePressed() == false){
new TrendliContract.DisLike().execute(f);
f.setDislikePressed(true);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(--numOfLikes));
//v.setBackgroundColor(R.color.black);
//b.setEnabled(false);
} else{
new TrendliContract.UnDisLike().execute(f);
f.setDislikePressed(false);
int numOfLikes = Integer.parseInt(holder.likesTextView.getText().toString().replace(" ", ""));
holder.likesTextView.setText(String.valueOf(++numOfLikes));
//v.setBackgroundColor(R.color.transparent);
//b.setEnabled(true);
}
}
});
convertView.setTag(holder);
return convertView;
}
private class FeedHolder{
TextView titleTextView;
TextView usernameTextView;
TextView likesTextView;
Button likeButton;
Button unlikeButton;
VideoView video;
FrameLayout frameLayout;
}
}
You must never change the properties of ListView item's view in the onClick event.
The reason is that the onClick event get called out of getView method in a different time and because of recycling system of ListView those affected properties will be applied to recycled views, so you get irrelevant results.
Instead you must store the properties in an array and then in the getView method use that array to apply properties to views...
In the Adapter class
boolean[] buttonState;
In the constructor:
buttonState = new boolean[feeds.size()];
In the onClick:
buttonState[position] = false; // or true...
In the getView:
holder.yourButton.setEnabled(buttonState[position]);
As a variant you can create custom button class extends Button and implements View.OnClickListener, then override it's onClick method to set background corresponding with the flag like mPressed. After that you can use this class for yours buttons. Here an example of custom button class:
public class CustomButton extends Button implements View.OnClickListener {
private boolean mPressed = false;
private static final int mRegularBackID = R.drawable.button_up;
private static final int mPressedBackID = R.drawable.button_down;
public CustomButton( Context context ) {
super( context );
}
public CustomButton( Context context, AttributeSet attrs ) {
super( context, attrs );
}
public CustomButton( Context context, AttributeSet attrs, int defStyle ) {
super( context, attrs, defStyle );
}
#Override
public void onClick( View view ) {
mPressed = !mPressed;
this.setBackgroundResource( mPressed ? mPressedBackID : mRegularBackID );
}
}
Instead of static drawable IDs with the flag (as in my example) you can also use drawable state list or constructor with corresponding drawables as additional parameters.
You can use this custom class in XML with full package name like
<my.own.project.package.CustomButton... />
View passed in onClick(View v) it's clicked Button object you can use it to change Button property.
public void onClick(View v) {
Button b = ((Button)v);
b.setEnabled(false);
}
You can setup background property of Button inside your xml layout. Even you can define drawable xml slector witch will define different background for different states like: pressed, focused, disabled, etc.
First you have to define selector xml and set it as Button background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/selected" android:state_pressed="true"/>
<item android:drawable="#color/selected" android:state_selected="true"/>
<item android:drawable="#android:color/transparent"/>
</selector>
Instead of colors you can use drawable resources (images or shapes). Also you can define different states in this list. First element in selector witch match current satte will be used.
Try --
likeButton.setBackgroundColor(R.color.transparent);
unlikeButton.setEnabled(false);
I'm implementing a container with 3 buttons, added dynamically. When I select one, I want that the other ones are deselected (change drawable). This works when I add the buttons directly to a LinearLayout, which I declared in XML. But when I create a custom layout, which extends LinearLayout, and put my buttons there, it doesn't work anymore (although the code is almost the same). I select one button and the code to deselect the other ones is executed, but they still show the drawable of selected state.
Edit: I always can select the buttons, in both cases. Selection works. What doesn't work in the custom view case is de -selecting the views, or at least the drawable is not changed when the views are deselected.
I reduced the code to the minimal necessary to see the problem.
The activity:
public class TestActivity extends Activity {
private View[] views;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
/////////////////////////////////////////////////////////////////
//uncomment this block to see the working code (without custom layout)
// views = new View[3];
// LinearLayout root = (LinearLayout) findViewById(R.id.container);
// findViewById(R.id.myCustomLayout).setVisibility(View.GONE);
// addButton(root, 0);
// addButton(root, 1);
// addButton(root, 2);
//////////////////////////////////////////////////////////////////
}
private void addButton(final ViewGroup root, final int position) {
View v = new View(this);
LinearLayout.LayoutParams viewLayoutPars = new LinearLayout.LayoutParams(150, 200);
v.setLayoutParams(viewLayoutPars);
v.setBackgroundResource(R.drawable.mybg);
if (position == 0) {
v.setSelected(true);
}
root.addView(v);
views[position] = v;
v.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
for (View view : views) {
view.setSelected(false);
}
v.setSelected(true);
root.invalidate();
}
});
}
}
The layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.test.MyCustomLayout
android:id="#+id/myCustomLayout"
xmlns:dg="http://schemas.android.com/apk/res/com.test"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
My custom layout:
public class MyCustomLayout extends LinearLayout {
private Context context;
private View[] views;
public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
views = new View[3];
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
addButton(0);
addButton(1);
addButton(2);
}
private void addButton(final int position) {
View v = new View(context);
LinearLayout.LayoutParams viewLayoutPars = new LinearLayout.LayoutParams(150, 200);
v.setLayoutParams(viewLayoutPars);
v.setBackgroundResource(R.drawable.mybg);
if (position == 0) {
v.setSelected(true);
}
addView(v);
views[position] = v;
v.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
for (View view : views) {
view.setSelected(false);
}
v.setSelected(true);
invalidate();
}
});
}
}
The drawable (mybg):
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="#drawable/selected" />
<item android:drawable="#drawable/not_selected" />
</selector>
The images are 2 harmless squares selected.png and not_selected.png with different color.
Thanks in advance.
i have a simple question:
suppose i have some views on a scrollView (or a horizontalScrollView) .
is there any way to add a listener that will tell me when such a view is getting inside and outside the visible area ?
the only similar question i've seen is this:
Android: how to check if a View inside of ScrollView is visible?
but i want to be informed when such an event occurs (becoming hidden/visible) .
Subclass the view classes you are using (I did this for ImageView as I was only adding those to my scroll view):
public class PeekImageView extends ImageView implements ViewTreeObserver.OnScrollChangedListener {
private static final String LOG_TAG = "PeekImageView";
private InViewportListener inViewportListener;
private boolean isInViewport = false;
public PeekImageView(Context context) {
super(context);
}
public PeekImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PeekImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public interface InViewportListener {
void onViewportEnter(PeekImageView view);
void onViewportExit(PeekImageView view);
}
public void setInViewportListener(InViewportListener listener) {
this.inViewportListener = listener;
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
ViewTreeObserver vto = getViewTreeObserver();
if (vto != null) {
vto.addOnScrollChangedListener(this);
}
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
ViewTreeObserver vto = getViewTreeObserver();
if (vto != null) {
vto.removeOnScrollChangedListener(this);
}
}
#Override
public void onScrollChanged() {
Rect bounds = new Rect();
boolean inViewport = getLocalVisibleRect(bounds);
Log.d(LOG_TAG, "is in view " + bounds + " : " + inViewport + " ; " + bounds);
if (inViewportListener != null && isInViewport != inViewport) {
if (inViewport) {
inViewportListener.onViewportEnter(this);
} else {
inViewportListener.onViewportExit(this);
}
}
isInViewport = inViewport;
}
}
Attaching an InViewportListener to an instance of this PeekImageView will get you notified whenever the view enters or leaves the visible part of the window (the viewport).
You could do something like:
1) keep a list/array of views that are contained in your ScrollView.
2) Set a listener on the scroll view for when the scroll is changed: Synchronise ScrollView scroll positions - android
3) In the listener loop through these views using the Android: how to check if a View inside of ScrollView is visible? method to see if they have gone of the screen
This is a basic method but it'll work, how fast it is depends on whats on your screen etc, but it starts you in the right direction
I've found a nice way to be notified of what i've asked about here.
it works for scrollView with vertical LinearLayout, but if you wish you can make it work for other cases too, depending on the case.
i'm not sure if i should handle onSizeChanged() method too, and if so, what to do there, but in all other cases, this code works fine.
here's the code:
MainActivity.java (for testing) :
public class MainActivity extends Activity
{
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final CustomScrollView scrollView=(CustomScrollView)findViewById(R.id.scrollView1);
scrollView.setOnChildViewVisibilityChangedListener(new onChildViewVisibilityChangedListener()
{
#Override
public void onChildViewVisibilityChanged(final int index,final View v,final boolean becameVisible)
{
Log.d("Applog","index:"+index+" visible:"+becameVisible);
}
});
final ViewGroup container=(ViewGroup)findViewById(R.id.linearLayout);
for(int i=0;i<20;++i)
{
final TextView tv=new TextView(this);
tv.setText("item "+i);
tv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,300));
container.addView(tv);
}
}
}
activity_main.xml
<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" >
<com.example.scrollviewvisibilitydetector.CustomScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</com.example.scrollviewvisibilitydetector.CustomScrollView>
</RelativeLayout>
CustomScrollView.java (the real deal...) :
public class CustomScrollView extends ScrollView
{
Set<Integer> _shownViewsIndices =new HashSet<Integer>();
onChildViewVisibilityChangedListener _onChildViewVisibilityChangedListener;
public interface onChildViewVisibilityChangedListener
{
public void onChildViewVisibilityChanged(int index,View v,boolean becameVisible);
}
public CustomScrollView(final Context context)
{
super(context);
}
public CustomScrollView(final Context context,final AttributeSet attrs)
{
super(context,attrs);
}
public CustomScrollView(final Context context,final AttributeSet attrs,final int defStyle)
{
super(context,attrs,defStyle);
}
public void setOnChildViewVisibilityChangedListener(final onChildViewVisibilityChangedListener onChildViewVisibilityChangedListener)
{
_onChildViewVisibilityChangedListener=onChildViewVisibilityChangedListener;
}
#Override
protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
{
super.onLayout(changed,l,t,r,b);
checkViewsVisibility(l,t);
}
private void checkViewsVisibility(final int l,final int t)
{
final ViewGroup viewGroup=(ViewGroup)getChildAt(0);
final int childCount=viewGroup.getChildCount();
if(childCount==0)
return;
final int parentBottom=t+getHeight();
// prepare to use binary search to find a view that is inside the bounds
int min=0,max=childCount-1,piv=-1;
int childTop,childBottom;
View v;
// check previously shown views
for(final Iterator<Integer> iterator=_shownViewsIndices.iterator();iterator.hasNext();)
{
final Integer cur=iterator.next();
v=viewGroup.getChildAt(cur);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
{
if(piv==-1)
piv=cur;
}
else
{
if(_onChildViewVisibilityChangedListener!=null)
_onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(cur,v,false);
iterator.remove();
}
}
if(piv==-1)
{
// check first view
v=viewGroup.getChildAt(min);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
piv=min;
else
{
// check last view
v=viewGroup.getChildAt(max);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
piv=min;
}
if(piv==-1)
while(true)
{
piv=(min+max)/2;
v=viewGroup.getChildAt(piv);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t)
break;
if(max-min==1)
return;
if(childBottom<t)
// view above bounds
min=piv;
else max=piv;
}
}
//
for(int i=piv;i<childCount;++i)
{
v=viewGroup.getChildAt(i);
childTop=v.getTop();
childBottom=v.getBottom();
// _shownViewsIndices.
if(childTop<=parentBottom&&childBottom>=t&&!_shownViewsIndices.contains(i))
{
_shownViewsIndices.add(i);
if(_onChildViewVisibilityChangedListener!=null)
_onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(i,v,true);
}
}
for(int i=piv-1;i>=0;--i)
{
v=viewGroup.getChildAt(i);
childTop=v.getTop();
childBottom=v.getBottom();
if(childTop<=parentBottom&&childBottom>=t&&!_shownViewsIndices.contains(i))
{
_shownViewsIndices.add(i);
if(_onChildViewVisibilityChangedListener!=null)
_onChildViewVisibilityChangedListener.onChildViewVisibilityChanged(i,v,true);
}
}
}
#Override
protected void onScrollChanged(final int l,final int t,final int oldl,final int oldt)
{
super.onScrollChanged(l,t,oldl,oldt);
checkViewsVisibility(l,t);
}
}
I'm trying to bind data from my SQLiteDatabase to a ListView. I'm currently using a SimpleCursorAdapter to fill in my ListView. Unfortunately this doesn't seem to work with setting a CheckBox's checked attribute.
This is how I do it now; instead of changing the CheckBox's checked status the adapter is filling in the value to the text argument, so the value is displayed right of the CheckBox as text.
Java:
setListAdapter( new SimpleCursorAdapter( this,
R.layout.mylist,
data,
new String[] { Datenbank.DB_STATE, Datenbank.DB_NAME },
new int[] { R.id.list_checkbox, R.id.list_text }
) );
mylist.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="#+id/LinearLayout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<CheckBox android:text=""
android:id="#+id/list_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
></CheckBox>
<TextView android:text=""
android:id="#+id/list_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
></TextView>
</LinearLayout>
Edit: The field in the database is of course of type boolean and I've also tried to assign an id to the checked field to fill the value in.
You could set a custom SimpleCursorAdapter.ViewBinder:
SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(/* ur stuff */);
cursorAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if(columnIndex == 1) {
CheckBox cb = (CheckBox) view;
cb.setChecked(cursor.getInt(1) > 0);
return true;
}
return false;
}
});
The setViewValue method is invoked for every column you specify in the SimpleCursorAdapter constructor and gives you a good place to manipulate some (or all) of the views.
I'm not sure how you would do this aside from creating a custom Adapter that overrode newView/bindView or getView, depending on what you override (ResourceCursorAdapter is a good one).
Ok, so here's an example. I didn't test to see if it would compile because I'm at work, but this should definitely point you in the right direction:
public class MyActivity extends ListActivity {
MyAdapter mListAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Cursor myCur = null;
myCur = do_stuff_here_to_obtain_a_cursor_of_query_results();
mListAdapter = new MyAdapter(MyActivity.this, myCur);
setListAdapter(mListAdapter);
}
private class MyAdapter extends ResourceCursorAdapter {
public MyAdapter(Context context, Cursor cur) {
super(context, R.layout.mylist, cur);
}
#Override
public View newView(Context context, Cursor cur, ViewGroup parent) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return li.inflate(R.layout.mylist, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cur) {
TextView tvListText = (TextView)view.findViewById(R.id.list_text);
CheckBox cbListCheck = (CheckBox)view.findViewById(R.id.list_checkbox);
tvListText.setText(cur.getString(cur.getColumnIndex(Datenbank.DB_NAME)));
cbListCheck.setChecked((cur.getInt(cur.getColumnIndex(Datenbank.DB_STATE))==0? false:true))));
}
}
}
You can solve that problem by creating a custom CheckBox widget like so:
package com.example.CustomCheckBox;
public class CustomCheckBox extends CheckBox {
public CustomCheckBox(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomCheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomCheckBox(Context context) {
super(context);
}
protected void onTextChanged(CharSequence text, int start, int before, int after) {
if (text.toString().compareTo("") != 0) {
setChecked(text.toString().compareTo("1") == 0 ? true : false);
setText("");
}
}
}
The onTextChanged function will be called when the ListView binds the data to the CheckBox (ie. Adding either "0" or "1"). This will catch that change and add in your boolean processing. The 1st "if" statement is needed so as to not create infinite recursion.
Then provide your custom class in to the layout file as such:
<com.example.CustomCheckBox
android:id="#+id/rowCheckBox"
android:layout_height="fill_parent"
android:layout_width="wrap_content" />
That should do it!