1. *** New version of B4J is available ***
    B4J v7.8
    Dismiss Notice

B4A Library CustomView and Lib Example - RatingBar Using Mainly Reflection

Discussion in 'Additional libraries, classes and official updates' started by stevel05, May 14, 2013.

Similar threads

Other B4A v5.80 BETA is released!
B4A Question Rating Bar not showing stars
B4A Tutorial Google Maps
B4A Library RatingBar
B4A Tutorial Custom View with Designer Support
  1. stevel05

    stevel05 Expert Licensed User

    Create a Custom View from Android API using (mainly) Reflection - RatingBar

    Suggested pre reading:

    Reflection Documentation

    CustomView with designer support

    This is an example of what can be done using reflection and is intended mainly as a learning exercise (I always learn something when I build this sort of thing), although there is a lot of reflection code, it is not a time critical library so would probably be up to the task in general use. Time will tell.


    In a bid to help those that want to better understand Reflection and the new CustomView, I have created this walkthrough that uses both to implement a Class that is not currently available which is RatingBar.

    This is a simple Class, although I did find a couple of wrinkles as I tried to create this library and does not contain too many methods, although more could be added for inherited View methods if required.

    To dive straight in we need to find the relevant Android API documentation for something interesting, in this case the RatingBar. Which is here : RatingBar | Android Developers.

    Setting up the project

    Then we need to create our outline project. To do that, we need a new standard B4A project to which we add a class (Project/Add New Module/Class) in this case we will call it RatingBarClass, then we need change the initialize method and add a DesignerCreateView method which are required to add the customview with the designer. This is all documented here : CustomView with designer support

    Our starting class code should look something like this:
    Code:
    'Class module
    Sub Class_Globals
        
    Private mTarget As Object
        
    Private mEventName As String
    End Sub

    'Initializes the object. You can add parameters to this method if needed.
    Public Sub Initialize (TargetModule As Object, EventName As String)
       mTarget = TargetModule
       mEventName = EventName
    End Sub

    Public Sub DesignerCreateView(Base As Panel, Lbl As Label, Props As Map)
    End Sub
    Now we want to add our custom view to the designer:
    Open the designer and click AddView, select CustomView and change the name to RatingBar1, notice that the Event Name also changes. Then in the Custom Type field (below the + TextStyle field) select RatingBarClass. Save the Layout as 1.

    Adding the RatingBar

    Now for a bit of reflection. Add the reflection library to your project (download it and add it to your addl libs folder if you haven't already) click the Libs tab at the bottm right of the IDE, and select Reflection from the list, it should briefly say loading then display the version no and a tick in the box next to it.

    Take a look at the Android Developers documentation (link above), we need to find a Public Constructor so that we can create an instance of the RatingBar. There are three, we want to be able to select the size of the displayed stars, to do this we need to provide a default style so we need a constructor that will accept the style.

    Code:
    RatingBar(Context context, AttributeSet attrs, int defStyle)
    The Context class holds information about the applications environment and needs to be passed to the constructor to enable creation, the Reflection Library gives us access to the Context via it's GetContext method. We don't need to pass anything for the attribute set so we can use null, the defStyle we use to define the size of the stars displayed which is an integer number defined in the Android API.

    The Reflection library also provides two create object methods. As we need to pass parameters we need createobject2.

    This is a similar process to the Dim of B4a, it returns a pointer to the created object with which we can call more methods on it.

    Because Basic4Android doesn't know about all of the objects that could possibly be available from the Android API, we need to use a generic B4A type. In this case as our Object is a child of the View class we will use View so that we can use methods inherited from View as well as those specific to RatingBar.

    So in Class_Globals we put:
    Code:
    Dim RB As View
    Then create the object in DesignerCreateViews:
    Code:
    'Create RatingBar Object
    Dim R As Reflector
    Dim mSize As Int = 16842876
    RB=R.CreateObject2(
    "android.widget.RatingBar",Array As Object(R.GetContext,Null,mSize), Array As
    String("android.content.Context","android.util.AttributeSet","java.lang.int"))
    For reflection to be able to access the objects we need to provide the full qualified names which are available in the Android developers documentation and the Reflection documentation.

    Base is the panel that the designer passes to DesignerCreateView for us to build our view on, we can now add our RatingBar object RB to the Base Panel:
    Code:
    Base.AddView(RB,0,0,100%x,100%y)
    Using methods inherited from View we can then set the Width and Height to a value that is an android constant (although we can't access the constants directly) that means Wrap Content which ensures that the View is just big enough to display then number of stars we ask it to.

    Code:
    '-2 = Wrap Content, will make the correct number of starts appear
    RB.Width=-2
    RB.Height=-
    2
    Using the CustomView

    We can now set up our Main activity as we would normally to load a layout, i.e. Dim the view and load the layout as below:

    Code:
    Sub Process_Globals
       
    'These global variables will be declared once when the application starts.
       'These variables can be accessed from all modules.
    End Sub

    Sub Globals
       
    'These global variables will be redeclared each time the activity is created.
       'These variables can only be accessed from this module.
       Dim RB As RatingBarClass
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    'Do not forget to load the layout file created with the visual designer. For example:
       'Activity.LoadLayout("Layout1")
       Activity.LoadLayout("1.bal")
    End Sub
    And our class code looks like:
    Code:
    Sub Class_Globals
       
    Private mTarget As Object
          
    Private mEventName As String
       
    Dim RB As Object
    End Sub
    'Initializes the object. You can add parameters to this method if needed.
    Public Sub Initialize (TargetModule As Object, EventName As String)
       mTarget = TargetModule
       mEventName = EventName
    End Sub
    Public Sub DesignerCreateView(Base As Panel, Lbl As Label, Props As Map)
       
    'Create RatingBar Object
       Dim R As Reflector
       
    Dim mSize As Int = 16842876
        RB=R.CreateObject2(
    "android.widget.RatingBar",Array As Object(R.GetContext,Null,mSize), Array As String("android.content.Context""android.util.AttributeSet""java.lang.int"))
       Base.AddView(RB,
    0,0,100%x,100%y)
       
    '-2 = Wrap Content, will make the correct number of stars appear
       RB.Width=-2
       RB.Height=-
    2
    End Sub
    Compile and run this and you should see the rating stars in the panel on the screen. That's the hard work done.

    Now we can go through the rest of the methods available in the RatingBar Class and add then to our CustomView taking care to use the correct type names in the reflection calls. These are in the complete project download.

    As an example here is the get/setter code to get/set the number of stars note the lower case get and set at the beginning of the sub names:
    Code:
    'Get or set the number of stars shown.
    Sub getNumStars As Int
       
    Dim R As Reflector
       R.Target=RB
       
    Return R.RunMethod("getNumStars")
    End Sub
    Sub setNumStars(Num As Int)
       
    Dim R As Reflector
       R.Target=RB
       R.RunMethod2(
    "setNumStars",Num,"java.lang.int")
    End Sub
    I have created one class specific get/setter for visibility:
    Code:
    'Get or Set Rating Bar visibility
    Sub setIsVisible(Visible As Boolean)
       mBase.Visible=Visible
    End Sub
    Sub getIsVisible As Boolean
       
    Return mBase.Visible
    End Sub
    If you look in the IDE IsVisible will be one method.
    Set as:
    Code:
    RB.IsVisible = True
    And get as:
    Code:
    If RB.IsVisible Then ...
    Using the CustomView in code

    To add a custom view to code, we need to add a second initialize path. This avoids problems negotiating the parameters passed to DesignerCreateView by the designer.
    Here I have created a Sub called Setup, which mirrors the DesignerCreateSub and we can freely call it, after we have called initialize. I stress that this is only if you want to add this view manually, there is no need to run either if you add the View using the designer.

    Additional functionality

    Starsize

    There are two default star sizes, selecting them is difficult with the designer as there are no optional fields. For now this code will use the Tag field. Enter 'Small' in the tag to display small stars. The designer passes the settings in the Label View that is passed to DesignerCreateView which can therefore be accessed as
    Code:
    If Lbl.Tag = "Small" Then ...
    etc.

    Code for the selection has been added to the class.

    If you add the CustomView manually you need to call Initialize and Setup, one of the parameters for setup is size.

    RatingChanged CallBack

    This library will now work as it is, the only thing that is missing is the onRatingBarChangeListener. It is not possible to set this using reflection, so for the sake of completeness, I have provided a single method Java Library for the purpose, all it does is tie the eventName provided to the onRatingBarChangeListener so that when the stars are changed on screen, the sub eventName + "_RatingChanged" is called. This is a separate file in this post and it's jar and xml files should be added to the additional library files in the normal manner if you want to use it.

    Adding a callsub using mTarget and mEventName updated from the initialization subroutine which gives us the module from which the class instance was created and the eventname, we can redirect the callback to the relevant module and Subroutine.

    Code:
    'Callback from java lib
    Sub RB_RatingChanged(Rating As Float,UserChanged As Boolean)
       
       
    Log(Rating&" "&UserChanged)
       
    Dim SubToCall As String =mEventName&"_RatingChanged"
       
    CallSub3(mTarget,SubToCall,Rating,UserChanged)
    End Sub
    And to allow the events to be displayed in the designer add
    Code:
    #Event: RatingChanged(Rating As Float,UserChanged As Boolean)
    to the beginning of the class code.

    Files attached are the full project (RB.zip), and a Java Library which implements a RatingChanged callback (RatingBar.zip).

    Download and unzip the Java Library files to your addlLibs folder. if you don't want to install and use the callback library just comment out the lines:

    Code:
    Dim RBLib As RatingBarLib
    From Class_Globals and

    Code:
    RBLib.setCallback(RB,"RB")
    from the Setup and DesignerCreateView Subs

    The project contains two activities, one with the View added with the designer and one added manually, click anywhere on the screen apart from the stars to switch activities.

    I've enjoyed putting this together, I hope it's useful.

    Requirements:


    Versions:

    V1.1 RB.Zip added Event function and call to initializing module.
    RatingBar.Zip (Library) removed un-needed files from jar - reduced size

    V1.2 RB.zip implemented methods as get/setters - a little tidier.
     

    Attached Files:

    Last edited: May 16, 2013
  2. thedesolatesoul

    thedesolatesoul Expert Licensed User

    Very nice tutorial. Thanks stevel05!
     
  3. moster67

    moster67 Expert Licensed User

    @tds: yep, you're right. Steve has written a great tutorial. I will walk through it properly tomorrow.

    Thank you Steve. You have a nice way of explaining stuff - I've seen that in many of your helpful posts here at the forum. I wish you could partnership with that user who is writing the B4A-book. I think you would be a great asset for him.
     
  4. Erel

    Erel Administrator Staff Member Licensed User

    Thank you for sharing this great tutorial!
     
  5. stevel05

    stevel05 Expert Licensed User

    Thanks All,

    @Moster67 I hope it makes sense when you go through it.
     
  6. m643

    m643 Member Licensed User

    Hi Stevel,

    I have two questions: Is it possible to change the color of the stars? Other question is how I can disable the onclick event on the rating bar because I want the bar only to show the stars not to change.

    Thank you
     
  7. stevel05

    stevel05 Expert Licensed User

    To take the second question first, you just need to implement setIsIndicator(boolean isIndicator) with reflection (see the android documentation).

    Changing the color is more problematic as it uses pre-drawn images, there is a solution here which I haven't tried and which I'm not sure how to implement on B4A, or if it can bee. I'll look into it a bit further.
     
  8. stevel05

    stevel05 Expert Licensed User

    It's not possible to change the color of the stars as RatingBar expects the style do be defined in XML which B4A doesn't use.
     
  9. Tom Christman

    Tom Christman Active Member Licensed User

    Thanks very much for this tutorial..........as Moster67 said, you have a wonderful quality for structured tutorials. And many of us appreciate the candor and patience in your postings (ala Klaus) for those of us who struggle with the quirkiness of the Android OS and Erels great software and its applications.
     
    stevel05 likes this.
  10. Inman

    Inman Well-Known Member Licensed User

    Can we use the XMLLayoutBuilder library to change the color?
     
  11. stevel05

    stevel05 Expert Licensed User

    Inman likes this.
  12. scsjc

    scsjc Well-Known Member Licensed User

    hello, nice work.... can use that to vote my app on playstore-rate direct ???
     
  13. stevel05

    stevel05 Expert Licensed User

    It's just an input method, you would need to write the rest of the code.
     
  14. Chris Williams

    Chris Williams Member Licensed User

    I love your rating bar! just a question:

    Is there a way to change the shape (hearts as opposed to stars)?
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice