How do you implement a custom view in Android so that it properly supports, or works with, the messaging queue ?
I'm trying to emulate the behavior of the built-in views so that I can properly / normally update a custom view with data within onCreate.
Currently, my custom view has ad-hoc set/update functions to put data in them. The problem with this is that my view's children views are not initialized until the first time onMeasure is called, which is after onCreate exits (which I think is how the built-ins do it).
Therefore, I want to know what the general strategy is (ie, what methods to override) to update a custom view from onCreate in such a way that the updates go into the message queue and reach the view after they are properly instantiated (just like the built-ins) ?
Thanks.
Look at View.post():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView hello = ((TextView) findViewById(R.id.hello));
hello.post(new Runnable() {
#Override
public void run() {
hello.setText("Hello World!");
}
});
}
Related
Here's a simple app, I'm trying to create logs in the printToLogs method.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v("Log0","test");
}
public void printToLogs(View view){
TextView menuTextView1 = findViewById(R.id.menu_item_1);
TextView menuTextView2 = findViewById(R.id.menu_item_2);
TextView menuTextView3 = findViewById(R.id.menu_item_3);
String item1 = (String) menuTextView1.getText();
String item2 = (String) menuTextView2.getText();
String item3 = (String) menuTextView3.getText();
Log.v("Log1",item1);
Log.v("Log2",item2);
Log.v("Log3",item3);
}
but logs with the tags Log1, Log2, Log3 are not shown at all in the logcat, what does show up is the Log0 in the onCreate method, but the other ones in printToLogs never show up when I search for them at all. I attempted re-installing the app and restarting logging. That didn't work.
The menu items are: Mango sorbet, Blueberry pie, Chocolate lava cake. And yes, I tried searching for them, and they are not in the logcat either.
If this is your actual code, you aren't even calling printToLogs in the onCreate method. You should be more diligent before posting something simple like this.
Barring a serious runtime environment issue, this problem should be fairly easy to solve.
It seems as if the printToLogs(View view) method is to be executed in response to the user clicking a button. If so, try including the following line in your activity_main.xml if you haven't already:
android:onClick="printToLogs"
This will bind the button on the UI with the printToLogs(View view) method.
If, on the other hand, printToLogs(View view) is intended to be a standalone method (i.e. one that should execute regardless of user input) it should not accept a View as an argument. For your purposes, its parameter list should be completely empty. In other words, the method signature should read:
public void printToLogs()
Also, it should be called within the onCreate(Bundle savedInstances) method. Add the following to the onCreate(Bundle savedInstances) method:
printToLogs();
This will initiate the execution of the method as soon as the app begins to run.
Make sure the logcat filter is set to "Verbose" when testing like so: (img is link since apparently I need 10 rep. to embed images into answers directly)
logcat filter
heyy add your method/function name in your button by using property section or just android:onClick in your xml file and then it will be solved
I have a headache in current Android project. I want to detect the changing of the current page. For example, there is a TextView to display device time, which is updated per second. How to detect this change? I searched a lot on SO (thanks SO), but none works for me.
More information: I don't use standard Activity to create page. My way is:
All widgets are created into a View object which is then used to create a container object. After that, I just handle this container to draw on a canvas with a VSYNC callback Choreographer.FrameCallback periodically.
Indeed, it works to draw. All are ok. Except: I want to draw canvas only when the page's content changed. So back to my beginning question, how to detect this "changing" event? I am sure there is some kind of callback to handle this problem. I used the following solution, but onGlobalLayout is not called when textview's text changed.
CanvasAppViewContainer container;//CanvasAppViewContainer extends AbsoluteLayout
LayoutInflater li =(LayoutInflater)getService().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = li.inflate(resourceId, null);//passed a correct the layout id
container = new CanvasAppViewContainer(getService(), view, getWidth(), getHeight(), getSurface());
rootView = (LinearLayout) findViewById(R.id.rootView); //root element of layout
rootView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#Override
public void onGlobalLayout() {
Log.d("test", "onGlobalLayout");
}
});
BTW: Even if I register the view tree observer for the textview, onGlobalLayout is still not called.
Thanks
onGlobalLayout() is only called when the layout changes. When you want to detect a change in the text use TextView.addTextChangedListener()
I have a button called panel_close in my java code. In the setOnclickListener method I update the visibility of some other views. However, it seems sometimes the code does not update the visibility of the panels properly. Here is the code
panel_close.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
View temp=findViewById(R.id.show_panel);
temp.startAnimation(window_close);
temp.setVisibility(View.GONE);
compass.setVisibility(View.VISIBLE); // compass is defined as a field. Sometimes the code does not make this view visible
stats.setVisibility(View.VISIBLE); // stats is defined as a field. Sometimes the code does not make this view visible
control.setVisibility(View.VISIBLE); // control is defined as a field. Sometimes the code does not make this view visible
loadAllList(); // this is an AsyncTask
loadAllpins(); // this is an AsyncTask
}
});
Any comment will be appreciated.
You should look at running these changes on the UI thread to make sure that the modification are visible to the UI:
Android - using runOnUiThread to do UI changes from a thread
You should give a detailed info about your variables first and this could be happening because you are making the layout or something like that invisible or gone, which the children are inside of. This will cause them to remain invisible.
I am having trouble grasping a certain concept in Android UI design. The book I am referring to first uses the usual technique that Java programmers use to create UIs and that is to to create containers and add UI components to them and nest them as necessary.
Now, the book introduces a new concept where the entire UI was created using an XML file. The code is pasted below:
package com.oreilly.android.intro;
import android.app.Activity;
import android.os.Bundle;
/**
* Android UI demo program
*/
public class AndroidDemo extends Activity {
private LinearLayout root;
#Override public void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.main);
root = (LinearLayout) findViewById(R.id.root);
}
}
so basically, I can use any of them ?
Simple answer,yes, you can use either approach. However, there are some limitations to each such as there are layout properties that must be set in xml if you want to use them. I can't think of what any are off-hand but I can look them up.
For the most part, creating the layouts is much simpler to do in xml but you do have the option of setting Views and layouts in Java if you need to such as creating an unknown number of Buttons depending on some user-defined variable.
When you create your UI in xml then you inflate it in your Java code. This is normally done in onCreate() using
setContentView(R.layout.main);
as you see in your example. But it can also be done with an inflater.
The thing to remember here is to inflate your layout, using either method, before trying to initialize any views in the layout or you will get a NPE when trying to call a method on a View defined before inflating the layout it is contained in.
A correct way
**Examples of inflating views/layouts correctly**
Button mBtn;
public class AndroidDemo extends Activity {
private LinearLayout root;
#Override public void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.main);
root = (LinearLayout) findViewById(R.id.root);
btn = (Button) findViewById(R.id.buttonId); // Button is initialized after inflating the layout
}
}
Incorrect way
public class AndroidDemo extends Activity {
private LinearLayout root;
#Override public void onCreate(Bundle state) {
super.onCreate(state);
Button mBtn = (Button) findViewById(R.id.buttonId); // Button is initialized before inflating layout which will return null
setContentView(R.layout.main);
root = (LinearLayout) findViewById(R.id.root);
}
}
I added the above example because I have seen a lot of people make that mistake. So don't do it...you've been warned! :)
Not entirely sure what you're asking, but the two are interchangeable.
Most of the time your UI will be done via xml. In some cases though, the ui is heavily dependent of the data, so you may need to dynamically generate it.
It basically comes down to whichever is easiest for you at the time.
Yes.
But is preferable to use xml, it is more powerful, easier and will separate layout from your code.
Take a look at the docs:
http://developer.android.com/guide/topics/ui/declaring-layout.html
I want to upgrade a couple of my Android apps to use Fragments. Here is a basic situation:
I have an Activity and a ListActivity. I want to convert these using fragments using the compatibility packaging. Below is what I have in the onCreate method in the Activity. (The ListActivity has similar items so I am only using the Activity as the example ere in the conversion).
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
setContentView(R.layout.rate);
Item = getIntent().getExtras().getString("name");
Category = getIntent().getExtras().getString("category");
title = (TextView) findViewById(R.id.tvRateItem);
ratingsBar = (RatingBar) findViewById(R.id.theRatingBar);
title.setText(Item);
// Display list of reviews
new starTotalTask().execute();
new starRatingTask().execute();
final EditText etTweetReview = (EditText) findViewById(R.id.etTweetReview);
Button button = (Button) findViewById(R.id.theRatingBarButton);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// do stuff
}
}
});
Button BReviews = (Button) findViewById(R.id.bReviews);
BReviews.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// do stuff
}
});
}
So you see, I have a couple of Buttons, a couple of AsyncTasks, some getIntents that are grabbing data from other Activities, some "widget" declarations referencing to the XML.
I have the basic framework already setup with a FragmentActivity (from compatibility v4 package) with a couple of fragments it points to. In fact, I have a ViewPager, and tabs set up. The correct layouts are already setup as it should be to the new Fragment view. I just don't get how to move the data as seen above. I also understand the onCreateView is where you set the layout. Outside of that, where does everything else go? Not looking for code example as much as direction -- IF POSSIBLE.
Edit: My layout is based on this: http://thepseudocoder.wordpress.com/2011/10/13/android-tabs-viewpager-swipe-able-tabs-ftw/
especially interest in:
- Where do the RatingsBar and TextView (and other xml references) go?
- what about AsyncTask?
- and how do you handle passing around data in intents?
Almost everything will be identical. The key differences are:
You need to extend FragmentActivity in place of Activity.
You need to call getSupportFragmentManager() instead of getFragmentManager().
You need to call getSupportLoaderManager() instead of getLoaderManager().
The compatibility library does not support a ListFragmentActivity (or whatever). You'll need to use a FragmentActivity instead and set up the ListView in your code.