Android Tutorial Custom ListView library

moster67

Expert
Licensed User
Longtime User

Thanks for your reply.

Yes, changing MyListViewAdapter.this to pParent got your example of using Sender working (would not work with MyListViewAdapter.this).

However, I was trying another example using Sender which Erel posted in the AHQuickAction-thread and this is the one which causes the classcastexception. This example of using Sender still crashes even when using pParent.

The code I was to trying to use was the following one:

B4X:
Sub MyListView1_ItemClick(ItemIndex As Int, FirstDescription As String, SecondDescription As String, value As String)
   ac1.Show(GetViewAtPos(Sender,ItemIndex)) '- here I get classcastexception
end sub

Sub GetViewAtPos(LV As ListView, Position As Int) As View
    Dim r As Reflector
    Dim v As View
    r.Target = LV
    Dim first As Int
    first = r.RunMethod("getFirstVisiblePosition")
    v = r.RunMethod2("getChildAt", Position - first, "java.lang.int")
    Return v
End Sub

The problem seems to be Sender because if I modify the code as follows, then everything works:


B4X:
Sub MyListView1_ItemClick(ItemIndex As Int, FirstDescription As String, SecondDescription As String, value As String)
   ac1.Show(GetViewAtPos(ItemIndex)) ' without Sender, everything works and no classcastexception is generated
end sub

Sub GetViewAtPos(Position As Int) As View
    Dim r As Reflector
    Dim v As View
    r.Target = MyListView1 '- assigning MyListView1 directly works so therefore I think it is Sender which causes problems and not Reflection
    Dim first As Int
    first = r.RunMethod("getFirstVisiblePosition")
    v = r.RunMethod2("getChildAt", Position - first, "java.lang.int")
    Return v
End Sub

I am still curious what causes the classcastexception when used with Sender alone (different from your example of using Sender). It might be also that I am using Sender in B4A erroneously although same code works with normal ListView of B4A.
 
Last edited:

luthepa1

Member
Licensed User
Longtime User
That's good to know because in my main app I too am using ahquickaction with the getview method of reflection library. I might take a look at this since I need to rethink my approach on selection mode of custom listview adapter code.

Sent from my GT-I9300 using Tapatalk 2
 

luthepa1

Member
Licensed User
Longtime User

I have tried many things but I am unable to use the Sender method also, for the AHQuickAction plugin using the GetViewAtPos function that makes use of the Reflectionr library as per the code you posted. I am using the same code in my project as well.

If your ClassCastException error is like the one I see its because our custom list adapter class "MyListView" is not compatible with B4A SimpleListView class or something along those lines. So I changed the list view variable to MyListview types but I am still having no joy :sign0148:

In the library code I will then try pParent or pView as the Sender Object in the raiseEvent function. Then in B4A MyListView long click event I declare a variable as either ListView or MyListView or View (depending what I am sending back in raiseEvent function in library) and then using my variable say is equal to Sender (I think this is needed since in the raiseEvent in Library we are sending an Object). Example
B4X:
   Dim send As ListView
   send = Sender

   
   MyQuickAction.show(GetViewAtPos(send, Position))

And then depending on what type of variable I am passing in GetViewAtPos you need to change the type of the LV variable in GetViewAtPos function (e.g. LV As ListView or LV As View or LV As MyListView).

But no matter what I try I either get ClassCastException or MyListView cannot be cast to anywhere software simplelistview, or endless others.

Currently if I use
B4X:
   Dim send As View
   send = Sender

   
   MyQuickAction.show(GetViewAtPos(send, Position))


Sub GetViewAtPos(LV As View, Position As Int) As View 'Note: LV As View***
    Dim r As Reflector
    Dim v As View
    r.Target = LV
    Dim first As Int
    first = r.RunMethod("getFirstVisiblePosition")
    v = r.RunMethod2("getChildAt", Position - first, "java.lang.int")
    Return v
End Sub

I will get
java.lang.IllegalStateException: setContentView was not called with a view to display.

When passing either pParent or pView in raiseEvent sender object.

I'm stuck! I'm just a "hobby programmer" - LOL.

Love to hear any advice of solution from anyone.
 

warwound

Expert
Licensed User
Longtime User
I think the problem lies with the type of the Sender object.

pParent is not a View type, it is a MyListViewAdapter.
The example code comment is incorrect and should read:

B4X:
   @Override
   public void onItemClick(AdapterView<?> pParent, View pView, int pPosition, long pId) {
      /*   this is the method that must be implemented as part of the AdapterView.OnItemClickListener
       * 
       * pParent is the MyListViewAdapter instance
       * pView is the inflated ListItem
       * pPosition is the index of the clicked ListItem
       * pId is i think only of use with a CursorAdapter as it will be the row id of the clicked ListItem in the Cursor.
       */
      MyDataClass dataItem = mMyDataClassList.get(pPosition);
      Log.d("B4A", "The clicked ListItem item_title is '"+dataItem.get("item_title")+"'");
      mBA.raiseEvent(MyListViewAdapter.this, mEventName+"_itemclick", new Object[]{pPosition});
   }

If B4A does not recognise the type of the Sender object then using the B4A Object type is the way to go:

B4X:
Dim send As Object
send=Sender
' now use Reflection on send object

That should work as all types descend from the Java Object class.

A better solution which would do away with the need to use Reflection would be to update the library so that B4A recognises the MyListViewAdapter type and add methods to MyListViewAdapter that do what you want.

First you'd need to update the MyListViewAdapter class so it is not hidden from the B4A IDE:

B4X:
@ActivityObject
@ShortName("MyListViewAdaper")
public class MyListViewAdapter extends ArrayAdapter<MyDataClass> implements OnItemClickListener{
   // code removed
}

Now add new methods to that class:

B4X:
@ActivityObject
@ShortName("MyListViewAdaper")
public class MyListViewAdapter extends ArrayAdapter<MyDataClass> implements OnItemClickListener{

    public void DoSomething(){
        // do something

    }

}

And in B4A you can now Dim a MyListViewAdapter and call it's public methods:

B4X:
Dim send As MyListViewAdapter
send=Sender
send.DoSomeThing

The same applies if you want to use the MyListView or MyDataClass types in B4A - remove the @Hide attribute and add an @ShortName attribute and then add public methods to the class to do as you want.

It all depends upon what you want to do.
If you want to show a QuickAction on the clicked item i'd have thought this would work:

MyListViewWrapper:

B4X:
@Events(values = { "ItemClick(ItemIndex As Int, ClickedItem As View)" })

MyListViewAdapter:

B4X:
   @Override
   public void onItemClick(AdapterView<?> pParent, View pView, int pPosition, long pId) {
      /*   this is the method that must be implemented as part of the AdapterView.OnItemClickListener
       * 
       * pParent is the MyListViewAdapter instance
       * pView is the inflated ListItem
       * pPosition is the index of the clicked ListItem
       * pId is i think only of use with a CursorAdapter as it will be the row id of the clicked ListItem in the Cursor.
       */
      mBA.raiseEvent(MyListViewAdapter.this, mEventName+"_itemclick", new Object[]{pPosition, pView});
   }

In B4A:

B4X:
Sub MyListView1_ItemClick(ItemIndex As Int, ClickedItem As View)
    MyQuickAction.show(ClickedItem)
End Sub

No need to use the Sender when you can simply pass the required object as a parameter.

Martin.
 

luthepa1

Member
Licensed User
Longtime User
Thanks Martin for spending the time to read the issues I am having.

Unfortunately still no joy. I have change library code back to the way it was originally (e.g raiseEvent sender object is now back to MyListViewAdapter.this).

If B4A does not recognise the type of the Sender object then using the B4A Object type is the way to go:

Code:
Dim send As Object
send=Sender
' now use Reflection on send object

Tried this but now I get
java.lang.ClassCastException: drp.mylistview.arrayadapter.MyListViewAdapter cannot be cast to anywheresoftware.b4a.objects.ListViewWrapper$SimpleListView
I then tried using this solution of dimming Send as ListView/MyListView/view all of which have IllegalStateException Errors of setContent was not called with view to display, or ClassCastExceptions errors or NoSuchMethodException getFirstVisiblePosition depending on what type I am using (NOTE: I also change the LV variable type to match my send variable type as well as now trying send = ClickedItem instead of sender since that did not work).

Now to try next suggestion

I had tried this also but when I pass the View directly to MyQuickAction.Show() I now get the error
java.lang.IllegalStateException: setContentView was not called with a view to display.
Weird!?? I am starting to wonder if the issue lies in the AHQuickAction library (no offence to author).


I guess what I need to do is with the first attempt that I tried above is I need the library MyListViewAdapter class to return the MyListView object in raiseEvent instead of MyListViewAdapter.this. I am not sure if this is possible or not as I am still learning about Android/Java?




The only way forward that I can see now is to try your other suggestion of unhiding the classes in the library and adding methods to execute directly from B4A.

back to the grind....

Thanks again Martin for your help so far

Paul.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
Did you update the MyListViewWrapper @Events attribute:

B4X:
@Events(values = { "ItemClick(ItemIndex As Int, ClickedItem As View)" })

What's your MyListViewAdapter onItemClick method look like?

ClickedItem (if this all works) should be the LinearLayout created from my_list_view_item.xml, a LinearLayout derives from ViewGroup which derives from View so i would have thought you could use pass that LinearLayout object to QuickAction's show() method.

What is the contents of your my_list_view_item.xml file?
Maybe you'd have more success passing the show() method a View that is a child of the LinearLayout instead of the LinearLayout itself?

Martin.
 

luthepa1

Member
Licensed User
Longtime User
Yeah not a bad point at all about passing a child view of my layout. Shall tie that a try. In the mean time here are my codes.

The wrapper class
B4X:
@Events(values = {"ItemClick(Position As Int, Value As Object)", 
              "ItemLongClick(Position As Int, ClickedItem As Object, Value As Object)",
              "OnScroll(firstVisibleItem As Int, visibleItemCount As Int, totalItemCount As Int)"})
@ShortName("MyListView")
@Version(1.2f)
public class MyListViewWrapper extends ViewWrapper<MyListView> {
   
   public void AddItem(String Title, String Description, String SessionCount, Bitmap ListThumbnailImage, Bitmap ListStatusImage){
      MyDataClass myDataClass=new MyDataClass();
      myDataClass.put("title", Title);
      myDataClass.put("description", Description);
      myDataClass.put("session_count", SessionCount);
      myDataClass.put("list_image", ListThumbnailImage);
      myDataClass.put("status", ListStatusImage);
      getObject().addItem(myDataClass);
   }
NOTE: I set ClickedItem as Object because if I state View I keep getting signature mismatch error at run time of app. In fact I always find this is the case with anything extra than Position As Int. I was going to try and find a more suitable post on this issue as to why I anything other than Position I can only declare as Object. Maybe this is my issue to everything? - lol.

Then the added listeners of MyListView class
B4X:
      this.setAdapter(mMyListViewAdapter);
      
      //   this is where we set the ListView's OnItemClickListener to the instance of the MyListViewAdapter which now implements 
      //  android.widget.AdapterView.OnItemClickListener
      //   note that you must set the OnItemClickListener AFTER setting the Adapter
      this.setOnItemClickListener(mMyListViewAdapter);
      this.setOnItemLongClickListener(mMyListViewAdapter);
      this.setOnItemSelectedListener(mMyListViewAdapter);
      this.setOnScrollListener(mMyListViewAdapter);
      //this.setMultiChoiceModeListener(mMyListViewAdapter);

      //   FastScroll hard coded to true
      this.setFastScrollEnabled(true);
      
   }

I will just provide all of MyListViewAdapter class
B4X:
@ActivityObject
@ShortName("MyListViewAdapter")
public class MyListViewAdapter extends ArrayAdapter<MyDataClass> implements OnItemClickListener, 
                                                         OnItemLongClickListener, 
                                                         OnItemSelectedListener,
                                                         OnScrollListener,
                                                         MultiChoiceModeListener{
   
   /*
    * This is the custom ArrayAdapter class.
    * 
    * http://developer.android.com/reference/android/widget/ArrayAdapter.html
    * 
    * Version 1.1 implements the android.widget.AdapterView.OnItemClickListener.
    * I had success implementing the View.OnItemClickListener but that required a lot of workaround code to identify the clicked View.
    * android.widget.AdapterView.OnItemClickListener is the interface intended to be used to handle clicks on adapter items.
    * 
    * http://developer.android.com/reference/android/widget/AdapterView.OnItemClickListener.html
    * 
    */
   
   //private MyListView MyListView;
   private BA mBA;
   private String mEventName;
   private int mItemLayoutResourceId;
   private LayoutInflater mLayoutInflater;
   private List<MyDataClass> mMyDataClassList;
   private String[] mTextViewIds;
   private String[] mImageViewIds;
   
   public MyListViewAdapter(BA pBA, int pItemLayoutResourceId, int pTextViewResourceId, List<MyDataClass> pMyDataClassList, String pEventName) {
      super(pBA.context, pItemLayoutResourceId, pTextViewResourceId, pMyDataClassList);
      
      mBA = pBA;   //Keep a reference to the Activity BA object
      mEventName = pEventName;
      mItemLayoutResourceId=pItemLayoutResourceId;
      
      /*
       * A reference to the LayoutInflater is kept to avoid calling it each time a new ListItem is required.
       * 
       * But look at the Log output - when the ListView is created the LayoutInflater is used, after that it is seldom used as ListItems are recycled.
       * If your ListItems vary in height then there is a chance the LayoutInflater will again be re-used.
       * 
       * You choose whether keeping a reference to it is a resource waste or not!
       * 
       */
      mLayoutInflater=LayoutInflater.from(pBA.context);
      
      mMyDataClassList=pMyDataClassList;
      
      /*
       * These are the ids of the textViews in my ListItem layout.
       * 
       * MyListViewWrapper uses the same ids to add values to a MyDataClass instance.
       * You'll probably want to use your own technique instead of my technique track TextView ids and Array values.
       * 
       */
      mTextViewIds=new String[]{"title", "description", "session_count"};
      mImageViewIds=new String[]{"list_image", "status"};
   }
   
   /*
    * A helper method to find a View in the ListItem.
    */
   private static View findViewById(View pView, String pId) {
      return pView.findViewById(BA.applicationContext.getResources().getIdentifier(pId, "id", BA.packageName));
   }
   
   @Override
   public View getView(int pPosition, View pConvertView, ViewGroup pParent) {
      /*
       * getView() is called when the ListView needs to make a ListItem visible.
       * 
       * A previously visible but no longer visible ListItem may be recycled otherwise a new ListItem is created.
       * 
       */
      
      if (pConvertView == null) {
         pConvertView = mLayoutInflater.inflate(mItemLayoutResourceId, null);
         Log.i("B4A", "New ListItem created");
      } else {
         Log.i("B4A", "Existing ListItem recycled");
      }
      MyDataClass dataItem = mMyDataClassList.get(pPosition);
      if (dataItem != null) {
         TextView textView;
         ImageView imageView;
         for(int i=0, j=mTextViewIds.length; i<j; i++){
            textView=(TextView)findViewById(pConvertView, mTextViewIds[i]);
            textView.setText((String) dataItem.get(mTextViewIds[i]));
         }
         for(int i=0, j=mImageViewIds.length; i<j; i++){
            imageView=(ImageView)findViewById(pConvertView, mImageViewIds[i]);
            //imageView.setDrawingCacheEnabled(true);
            //imageView.layout(0, 0, imageView.getWidth(), imageView.getWidth());
            //imageView.buildDrawingCache();
            imageView.setImageBitmap((Bitmap)dataItem.get(mImageViewIds[i]));
            //imageView.draw((Canvas) dataItem.get(mBitmapViewIds[i]));
            //imageView.setDrawingCacheEnabled(false);
         }
      }
      /*
       * If dataItem is null and this is a recycled View then the recycled View will retain it's old values..
       * 
       * It's probably not possible/likely that dataItem will be null?
       * 
       */
      return pConvertView;
   }

   @Override
   public void onItemClick(AdapterView<?> pParent, View pView, int pPosition, long pId) {
      /*   this is the method that must be implemented as part of the AdapterView.OnItemClickListener
       * 
       * pParent is the MyListViewAdapter instance
       * pView is the inflated ListItem
       * pPosition is the index of the clicked ListItem
       * pId is i think only of use with a CursorAdapter as it will be the row id of the clicked ListItem in the Cursor.
       */      
      
      MyDataClass dataItem = mMyDataClassList.get(pPosition);

      String returnStrings[] = {(String) dataItem.get("title"), (String) dataItem.get("description"), (String) dataItem.get("session_count")};
      
      mBA.raiseEvent(MyListViewAdapter.this, mEventName+"_itemclick", new Object[]{pPosition, returnStrings});
      Log.d("B4A", mEventName+"_itemclick");
   }

   @Override
   public boolean onItemLongClick(AdapterView<?> pParent, View pView, int pPosition, long pId) {
      MyDataClass dataItem = mMyDataClassList.get(pPosition);
      String returnStrings[] = {(String) dataItem.get("title"), (String) dataItem.get("description"), (String) dataItem.get("session_count")};
      
      mBA.raiseEvent(MyListViewAdapter.this, mEventName+"_itemlongclick", new Object[]{pPosition, pView, returnStrings});
      Log.d("B4A", mEventName+"_itemlongclick -- " + pParent);
      
      return true;      
   }
   
   @Override
   public void onItemSelected(AdapterView<?> pParent, View pView, int pPosition, long pId) {
      //mBA.raiseEvent(pParent, mEventName+"_itemselected", new Object[]{pPosition}); //, returnStrings);
      
      MyDataClass dataItem = mMyDataClassList.get(pPosition);
      dataItem.put("selected", true);
      
      Log.d("B4A", mEventName+"_itemselected = "+pPosition);
   }
   
    @Override
    public void onNothingSelected(AdapterView<?>  pParent)
    {

    }

    @Override
   public void onScroll(AbsListView mParent, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      mBA.raiseEvent(mParent, mEventName+"_onscroll", new Object[]{firstVisibleItem, visibleItemCount, totalItemCount});
      //Log.d("B4A", Integer.toString(firstVisibleItem));
   }

   @Override
   public void onScrollStateChanged(AbsListView mView, int scrollState) {
      // TODO Auto-generated method stub
      
   }

   @Override
   public boolean onActionItemClicked(ActionMode arg0, MenuItem arg1) {
      // TODO Auto-generated method stub
      return false;
   }

   @Override
   public boolean onCreateActionMode(ActionMode arg0, Menu arg1) {
      // TODO Auto-generated method stub
      return false;
   }

   @Override
   public void onDestroyActionMode(ActionMode arg0) {
      // TODO Auto-generated method stub
      
   }

   @Override
   public boolean onPrepareActionMode(ActionMode arg0, Menu arg1) {
      // TODO Auto-generated method stub
      return false;
   }

   @Override
   public void onItemCheckedStateChanged(ActionMode mMode, int mPosition, long mId, boolean mChecked) {
      Log.d("B4A", "Item " + mPosition + " Checked Status: " + mChecked);
      
   }
}

And that's pretty much the majority of code changes. And now the XML layout
B4X:
<?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="wrap_content"
    android:background="@drawable/list_selector"
    android:orientation="horizontal"
    android:padding="5dip" >
 
    <!--  ListRow Left side Thumbnail image -->
    <LinearLayout android:id="@+id/thumbnail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="3dip"
        android:layout_alignParentLeft="true"
        android:layout_marginRight="5dip">
        <ImageView
            android:id="@+id/list_image"
            android:layout_width="50dip"
            android:layout_height="50dip"
            android:src="@drawable/icon" >
            
        </ImageView>
    </LinearLayout>
 
    <!-- Title Of Exercise-->
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/thumbnail"
        android:layout_toLeftOf="@+id/status"
        android:layout_toRightOf="@+id/thumbnail"
        android:text="Main Title Heading..."
        android:textColor="#ffffffff"
        android:textSize="15dip"
        android:textStyle="bold"
        android:typeface="sans" />
 
    <!-- Description Name -->
    <TextView
        android:id="@+id/description"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title"
        android:layout_marginTop="1dip"
        android:layout_toLeftOf="@+id/status"
        android:layout_toRightOf="@+id/thumbnail"
        android:text="testing 123456789 the cat sat on the mat"
        android:textColor="#ffcccccc"
        android:textSize="10dip" />
 
    <!-- Rightend Workout Sessions Count -->
    <TextView
        android:id="@+id/session_count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignTop="@id/title"
        android:gravity="right"
        android:text="3"
        android:textColor="#ffcccccc"
        android:textSize="10dip"
        android:textStyle="bold" />
 
     <!-- Rightend Arrow -->
     <ImageView
         android:id="@+id/status"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentRight="true"
         android:layout_below="@id/session_count"
         android:layout_centerVertical="true"
         android:src="@drawable/ic_status" />
 
</RelativeLayout>
 

warwound

Expert
Licensed User
Longtime User
Try this:

MyListViewAdapter:

B4X:
   @Override
   public void onItemClick(AdapterView<?> pParent, View pView, int pPosition, long pId) {
      /*   this is the method that must be implemented as part of the AdapterView.OnItemClickListener
       * 
       * pParent is the MyListViewAdapter instance
       * pView is the inflated ListItem
       * pPosition is the index of the clicked ListItem
       * pId is i think only of use with a CursorAdapter as it will be the row id of the clicked ListItem in the Cursor.
       */
      MyDataClass dataItem = mMyDataClassList.get(pPosition);
      Log.d("B4A", "The clicked ListItem item_title is '"+dataItem.get("item_title")+"'");

      ViewGroup viewGroup=(ViewGroup) pView;

      //   mBA.raiseEvent(MyListViewAdapter.this, mEventName+"_itemclick", new Object[]{pPosition, viewGroup.getChildAt(0)});
      mBA.raiseEvent(viewGroup, mEventName+"_itemclick", new Object[]{pPosition});
      //   mBA.raiseEvent(pView, mEventName+"_itemclick", new Object[]{pPosition, viewGroup.getChildAt(1)});
      //   mBA.raiseEvent(viewGroup.getChildAt(0), mEventName+"_itemclick", new Object[]{pPosition, viewGroup.getChildAt(1)});
   }

And in B4A:

B4X:
Sub MyListView1_ItemClick(ItemIndex As Int)
   Log("MyListView1_ItemClick")
   Dim v As View
   v=Sender
   ac1.show(v)
End Sub

ac1 is an AHQuickAction object.
It works and the QuickAction menu appears and is anchored to the list item.

Casting the LinearLayout pView to a ViewGroup may be unnecessary as this also worked for me:

B4X:
mBA.raiseEvent(pView, mEventName+"_itemclick", new Object[]{pPosition});

If you uncomment the commented lines you'll see that you can open the QuickAction anchored to either of the two TextViews that are children of the LinearLayout.
(Remember to update the @Events attribute of you change the parameters passed.

My list item XML is:

B4X:
<?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="wrap_content"
   android:orientation="vertical" 
   android:paddingTop="5dip"
   android:paddingBottom="5dip"
   android:paddingLeft="5dip" 
   android:paddingRight="5dip">
   <TextView
      android:layout_height="wrap_content"
      android:layout_width="fill_parent"
      android:id="@+id/item_title"
      android:text="" 
      android:textSize="16dip">
   </TextView>
   <TextView
      android:layout_height="wrap_content"
      android:layout_width="fill_parent" 
      android:id="@+id/item_description"
      android:text="" 
      android:textSize="14dip">
   </TextView>
</LinearLayout>

I was thinking that you could add a plain View to a layout - the View would contain no content but your could pass it to B4A as the QuickAction anchor View.
Perhaps by using a RelativeLayout instead of a LinearLayout you could position a small View in your list item layout to control where the QuickAction menu opens?

B4A and library code attached.

Martin.
 

Attachments

  • MyListView_QuickAction.zip
    94.5 KB · Views: 743

luthepa1

Member
Licensed User
Longtime User
Good news! Finally sorted the issue!

Long story short, I had not initialised the QuickActions object component. Once again slapping my self!!!!! I had declared it which I guess is why I could get so far before experiencing runtime errors. Shame it took me so long to find and realise my mistake. The code for populating the list view and initialising and setup of the QuickActions object I had in their own sub routines. Had forgot to call sub to setup QuickAction.

I knew something was up when I tried your updated demo app with QuickAction component and found it to work without fault. I then tried you code suggestions in my demo app and still found no joy.

So today I used your latest demo app with QA and modified it to make use of my version of the MyListView library and found it to work! YAY! And I also quickly ruled out any issues to do with my xml layout. YAY again!

So all sorted. Sorry to have wasted your time although I kind of see it as the opposite as your help and guidance as taught me a lot about building and interfacing custom libraries with B4A. I had not found much info on libraries besides the simple tutes by Erel to which there seems to be good reason. And what you showed me on how to unhide the classes and add methods that I could execute from within B4A gives me ideas on how to tackle my next task of toggling selection mode on the list view .

So again, thanks Martin for all your help. Very much appreciated and hopefully in the not too distant future I can give back to the community ;-)

Paul.
 
Last edited:

moster67

Expert
Licensed User
Longtime User
@luthepa1

glad you got it sorted.

I haven't had time these days to try further. I will hopefully do so this evening.
 

hypergreatthing

Member
Licensed User
Longtime User
I've got a question regarding the custom listview.

I got the sample all up and running. Started mucking around with it.

My goal is to basically add a group to the layout for a questionnaire of sorts. Basically a header of a group title, then individual questions for that particular group.

So i played around with it, developed a layout that used a LinearLayout then a nested RelativeLayout. Got it running. But each time the additem is called (modified it to accept 4 parameters) it creates both a new Linear and Relative item.

What i'd like to do is to create only a new LinearLayout when the group name changes. That or create a function that will initialize the new group and another function for adding the questions below that group will work as well.

the second issue is that with the nested layouts the events stopped working. I'll look at the previous posts and see if i can figure that out.

I can post my new layout if that will help.
 

hypergreatthing

Member
Licensed User
Longtime User
Look back to this post: http://www.b4x.com/forum/basic4andr...17708-custom-listview-library.html#post101691



Your new layout contains items that are focusable perhaps?

Martin.
thanks for the insight on the events. I'll look into that.

Any idea on how to create a child layout and add it to a parent layout?

Basically it's something like this:

[Linear Layout]
Group Title
[Relative Layout]
Question Test 1 - description - button

Right now when it does the additem, it puts something like this:
Group 1
Question 1
Group 1
Question 2

When i want something like:
Group 1
Question 1
Question 2
etc...
Group 2
Question X
 

warwound

Expert
Licensed User
Longtime User
Hi again.

So your ListView item layout needs a variable number of Questions for each Group - or does every Group contain the same number of Questions?

The obvious solution - whether each Group contains the same number of questions or not - is to create an ExpandableListView B4A library instead of a ListView library.

Have a look at some screengrabs of the ExpandableListView: https://www.google.co.uk/search?q=a...g&biw=1920&bih=930&sei=dLckUL7OJqSf0QWeuYG4Cg

Does that look like it'd work better for you than a ListView?

desolatesoul has created an example here, it's not a java library - it's all done using B4A code.

I think to help you better you need to provide more detail about what you're trying to acheive.

If every Group has fixed number of Questions you could probably still use a the existing ListView example but would need to update the MyListViewWrapper.AddItem() and MyListViewAdapter.getView() methods.

Look at the MyDataClass.
Say each list item is a group title and a fixed number of questions.

B4X:
public void AddItem(String GroupTitle, String[] Questions){
   MyDataClass myDataClass=new MyDataClass();
   myDataClass.put("item_title", GroupTitle);
   myDataClass.put("item_questions", Questions);
   getObject().addItem(myDataClass);
}

That's from the MyListViewWrapper class, so in your B4A code you could call AddItem with a Group title and an array of Strings - each array element being a question.

You'd need to create an XML layout with a View to display the Group title and then a fixed number of other Views to display the Questions.

Have you seen DroidDraw?

WYSIWYG User Interface (UI) designer/editor/builder for cell phone and tablet application programming on the Android Platform

It's not perfect but you'll probably find it useful.

So you have an updated AddItem() method and a new XML layout, next you need to update the adapter getView() method.

B4X:
@Override
public View getView(int pPosition, View pConvertView, ViewGroup pParent) {
   /*
    * getView() is called when the ListView needs to make a ListItem visible.
    * 
    * A previously visible but no longer visible ListItem may be recycled otherwise a new ListItem is created.
    * 
    */
   if (pConvertView == null) {
      Log.i("B4A", "New ListItem created");
      pConvertView = mLayoutInflater.inflate(mItemLayoutResourceId, null);
   } else {
      Log.i("B4A", "Existing ListItem recycled");
   }
   MyDataClass dataItem = mMyDataClassList.get(pPosition);
   if (dataItem != null) {
      /* here you will have your MyDataClass instance which will have two properties: 'item_title' and 'item_questions'
      setting the pConvertView text fields will depend on what ids you have given them in the XML layout
      you should be able to easily set the item_title and then use a for statement to set the questions text (iterate through the 'item_questions' array)
      */
   }
   /*
    * If dataItem is null and this is a recycled View then the recycled View will retain it's old values..
    * 
    * It's probably not possible/likely that dataItem will be null?
    * 
    */
   return pConvertView;
}

That's all assuming each Group has the same number of Questions.
If the number of Questions in each group varies then using a ListView is going to be tricky - the ListView is designed so that each ListView item uses the same basic layout.
It's not suitable where each ListView item potentially requires a different layout - that's where an ExpandableListView would be much more suitable.

Martin.
 

hypergreatthing

Member
Licensed User
Longtime User
Thank you for your response. I think expandablelistview is more or less what i would like to do since the questions and categories will be dynamic and database driven. The number of groups and the questions per each group will change. The expandable part/collapsible part of the expandlistview isn't needed/wanted though (the arrow that allows you to collapse/expand them). And i really do like your custom library with the AHQuickActions. It makes it really neat/professional.

I did use DroidDraw to create my layout
B4X:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical"
   xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
   android:id="@+id/item_group"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="item_group"
   android:textSize="16sp" />
<RelativeLayout
   android:id="@+id/widget125"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:paddingLeft="15dp">
<TextView
   android:id="@+id/item_title"
   android:layout_width="375dp"
   android:layout_height="fill_parent"
   android:text="item_title"
   android:layout_alignParentTop="true"
   android:layout_alignParentLeft="true" />
<TextView
   android:id="@+id/item_description"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:text="item_description"
   android:layout_alignParentTop="true"
   android:layout_toRightOf="@+id/item_title" />
<Button
   android:id="@+id/item_count"
   android:layout_width="50dp"
   android:layout_height="50dp"
   android:text="0"
   android:layout_alignParentTop="true"
   android:layout_alignParentRight="true" />
</RelativeLayout>
</LinearLayout>
That's my layout. The idea was to create multiple relative layouts inside a single linear layout.

I think the addition of the button caused the events to stop triggering. I think changing that to a text field would work for my purposes. I just wanted a counter for how many times a particular question was selected.

Thank you so much. I think you've helped me figure out how to approach this.
 

Informatix

Expert
Licensed User
Longtime User

Maybe this class can help you: CheckList
 

Sinan Tuzcu

Well-Known Member
Licensed User
Longtime User
Helo,

it is possible to add an image as ListView1.AddTwoLinesAndBitmap for User Picture?

and

is it possible to add in the listview balloon like Whatsapp chat?
and the text in balloon ?

gruß
sinan
 
Last edited:

warwound

Expert
Licensed User
Longtime User
Hi.

Yes that sounds possible.

First step would be to edit the XML layout file that you use for your ListView items and add an Android ImageView, be sure to give this ImageView an id attribute.

Update the MyListViewWrapper AddItem() method so that it accepts a Bitmap as well as the item title and description, adding the Bitmap to the instance of MyDataClass along with the title and description values.

Then you need to update MyListViewAdapter so that it creates a reference to the ImageView id just as it creates references to the TextView ids (mTextViewIds).

In the adapter class you then need to update the getView() method.
After the TextViews have had their text values set you'd want to use findViewById() to find the ImageView (in pConvertView) and set an image. The image would be obtained from the instance of MyDataClass.

I think that outlines the steps required - it's tricky to say the exact steps required without seeing your source code.

So i'll leave it to you to try and modify your source and if you need more help then you will need to upload your source and then i'll give you some detailed instructions.

Martin.
 

Sinan Tuzcu

Well-Known Member
Licensed User
Longtime User
Hello Martin,

I am a newbie in B4A

Can you create an example for me?

Her my code, to add text and Image in Listview:
B4X:
Sub LogTable1

Dim Cursor1 As Cursor
Dim Spalte(5) As String
Dim Cursor2 As Cursor
Dim Cursor3 As Cursor
Dim Cep As String
Dim NickName As String
Dim lvd As ListViewData

ListView1.Clear
ListView1.Initialize("ListView1")
ListView1.TwoLinesAndBitmap.ImageView.Height = 55dip
ListView1.TwoLinesAndBitmap.ImageView.Width = 55dip
ListView1.TwoLinesAndBitmap.ItemHeight = 65dip
ListView1.TwoLinesAndBitmap.Label.TextSize = 20
ListView1.TwoLinesAndBitmap.Label.Left = 80dip
ListView1.TwoLinesAndBitmap.SecondLabel.TextSize = 13
ListView1.TwoLinesAndBitmap.SecondLabel.Left = 80dip
ListView1.TwoLinesAndBitmap.Label.TextColor = Colors.Black
ListView1.TwoLinesAndBitmap.SecondLabel.TextColor=Colors.DarkGray
ListView1.TwoLinesAndBitmap.Label.Gravity = Gravity.LEFT
ListView1.TwoLinesAndBitmap.Label.Typeface = Typeface.DEFAULT_BOLD
ListView1.ScrollingBackgroundColor = Colors.Transparent
ListView1.FastScrollEnabled = True
ListView1.Color=Colors.RGB(255,255,255)


Cursor1 = SQL1.ExecQuery("SELECT UserID, UserName, mesaj, LinkAdres, Datum FROM " & Cep)
               
         lvd.Initialize
         Cursor1.Position = Cursor1.RowCount - 1
         lvd.ErsteSpalte = Cursor1.GetString("UserID") 
             Spalte(1) = Cursor1.GetString("UserName")
           Spalte(2) = Cursor1.GetString("Mesaj")
           Spalte(3) = Cursor1.GetString("LinkAdres")
           Spalte(4)= Cursor1.GetString("Datum")   


 If File.Exists(codlar.ProfilResimYeri,lvd.ErsteSpalte & ".png") = True Then ListView1.AddTwoLinesAndBitmap2(NickName,Spalte(2) & "          " & Spalte(4), LoadBitmap(codlar.ProfilResimYeri, lvd.ErsteSpalte & ".png"),lvd)   
         Else
ListView1.AddTwoLinesAndBitmap2(NickName,Spalte(2) & "          " & Spalte(4),LoadBitmap(File.DirAssets, "appicon.png"),lvd)
         End If
Cursor1.Close

Activity.AddView(ListView1, 0, 0, 100%x, 100%y)
ListView1.BringToFront

End Sub


Listview1 Get the Text from Local SQlite Database
 

Informatix

Expert
Licensed User
Longtime User
It seems that Warwound and you are talking of two different listviews. You can't do what you want with the standard B4A ListView.

EDIT:
Explanations: the standard B4A listview can display only two different pictures. One in the background with TwoLinesAndBitmap.Background and one in the ImageView with TwoLinesAndBitmap.ImageView.Bitmap. You can display your balloon in the background, but it will have always the same orientation. You can't have one with a color and an arrow to the left, and another with a different color and an arrow pointing to the right. That's why you have to use another class to do it. Suggestions: CustomListView (the class, not this lib), CheckList, UltimateListView. You can also do it with the lib in this thread (I presume) but you need java programming skills.
 
Last edited:
Cookies are required to use this site. You must accept them to continue using the site. Learn more…