This should be very easy to do, but somehow after some 15 minutes searching I still can't get the answer:
I want to make a custom Android View combining a TextView and a Button, plus some customized behaviors/methods, let's say when I click the button it should change the TextView to "Hello, world!".
I know that I'll have to extends the View class, and design the layout in a XML, then do some magic to link the two. Could you tell me what the magic is? I know how to do this in an Activity, but not in a custom View.
EDITED
Ok, so I found out I need to use a Inflater to inflate my class with the child views defined in a layout. Here's what I got:
public class MyView extends View {
private TextView text;
private Button button;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
View.inflate(context, R.layout.myview, null);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
text = (TextView) findViewById(R.id.text);
button = (Button) findViewById(R.id.button);
}
}
However, the text and button child views were null. Any idea? (The XML is very simple without any fancy edit, i just grabbed a TextView and a Button from the eclipse toolbar and throw in.)
Ok, so the answer to my own question is: (i) go get dinner, (ii) extends LinearLayout instead of View, this makes it a ViewGroup and thus can be passed into the inflate(...) method but don't have to override the onLayout(...) method. The updated code would be:
public class MyView extends LinearLayout {
private TextView text;
private Button button;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
View.inflate(context, R.layout.myview, this);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
text = (TextView) findViewById(R.id.text);
button = (Button) findViewById(R.id.button);
}
}
Related
Background
I have an App with multiple custom TextViews in it.
These four Custom TextViews are all set up like this:
public class TextView_Light extends android.support.v7.widget.AppCompatTextView {
public TextView_Light(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public TextView_Light(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TextView_Light(Context context) {
super(context);
init();
}
public void init() {
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "fonts/Roboto-Light.ttf");
setTypeface(tf ,1);
}}
I do have TextView_Bold, TextView_Light, TextView_Regular and TextView_Thin. Each of them uses another font which is saved in assets/fonts
I use the TextViews like that:
<de.mayr.wap.app.helper.TextView_Light
android:id="#+id/textView_Light7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/overviewViaBarcode"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
When the App is started and a view shows up for the first time everything looks great, like that:
The Problem
In this view the user klicks on one of the tablerows, an Dialog appears where the user types in some data. On save the onResume method of the view is called, which calls an Webservice etc. like that:
#Override
public void onResume() {
super.onResume();
callSerNrWS();
}
public void callSerNrWS() {
if (CheckNetwork.isNetworkAvailable(getContext())) {
final AsyncResponseFortschritt<ResponseObject<PruefauftraegeVO>> response = new AsyncResponseFortschritt<ResponseObject<PruefauftraegeVO>>() {
#Override
public void processFinishSerNr(ResponseObject<PruefauftraegeVO> output) {
anw = createPruefAnwArraylist();
seriennummerVO = findDataToSerNr();
adapt = new PruefAnwAdapter(getContext(), anw, seriennummerVO);
pruefListView.setAdapter(adapt);
setVisibilityLoadAnimation(View.GONE);
if (output.getException() != null && !output.getException().isEmpty())
AlertCreator.makeFailAlert(R.string.titelKeinFortschritt, R.string.textKeinFortschritt,getContext());
}
#Override
public void processStartSerNr() {
setVisibilityLoadAnimation(View.VISIBLE);
}
};
AsyncCallSerNrWS ws = new AsyncCallSerNrWS(response, pruefauftraegeVO);
ws.execute();
}
}
And after that, theres in 90% of cases one or more TextView which changed its looking from eg. TextView_Light to TextView_Thin. Like in the following picture: The appearence of the 'Anzugstrom [A]' in the third row now looks like a TextView_Thin and the '9/48' in the fifth row looks like a TextView_Light
What I know
This doesnt happen when the view is shown for the first time
There isn't any system behind it. It's totally random.
It happens in activitys and fragments
It happens in every view i have whatever the TextView contains 'hard-coded' text or text that comes from an Webservice
Its not just in lists, also in 'normal' views
I'm developing a View that shows a stack of messages making fadeIn,fadeOut and wait time between them. It has a stack of messages and when I need to display some of them I added to it. This view hasn't a specific design so the user can customize like he wants.
I have written well the 3 super constructors.
The XML layout is the next:
[... Others views ...]
<com.example.lectorrss.Views.StatusMessage
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/statusMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:background="#6894ed">
<ProgressBar
android:id="#+id/progressBar"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/message"
android:layout_toRightOf="#id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Hola"
android:textColor="#fff"></TextView>
</RelativeLayout>
</com.example.lectorrss.Views.StatusMessage>
[... Other views ...]
In this case, I want my custom view shows the views that are inside (progress bar + textview) and then apply the behaviour I want.
The code of my CustomView is:
public class StatusMessage extends ViewGroup{
private List<String> messagesStack;
private Worker worker;
public StatusMessage(Context context) {
super(context);
}
public StatusMessage(Context context, AttributeSet attrs){
super(context, attrs);
}
public StatusMessage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
TextView txtView = (TextView) this.findViewById(R.id.message);
if(txtView == null){
Log.d("BrowsePortal", "TextView null");
}else{
// HERE, THE CODE CAN FIND OK THE VIEW I WANT
Log.d("BrowsePortal", "TextView not null");
}
worker = new Worker(this);
worker.execute();
}
[Other logic code about view]
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
But as my view needs time of wait between messages, and uses animation, I need to create an AsyncTask:
public class Worker extends AsyncTask<Void,String,Void>{
private StatusMessage view;
public Worker(StatusMessage view) {
this.view = view;
}
#Override
protected Void doInBackground(Void... arg0) {
[I supressed all logic code here]
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
TextView txtView = (TextView) view.findViewById(R.id.message);
if(txtView == null){
// But here the code return null
Log.d("BrowsePortal", "TextView null");
}else{
Log.d("BrowsePortal", "TextView not null");
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
[I supressed all logic code here]
}
}
My problem is that from asynctask, on the progressUpdate, when I need to access to the textview, findviewbyid returns null. But trying to access the same view from custom view class instead asynctask, it works. Why from view class I can access to textview but from asynctask not?
Also, in post execute, the same code returns me null.
My asynctask receives well in her constructor the view so from here would work well as from customview.
The issue was with this line:
import android.R which I change to import [app_package].R;.
Then, when I put this line:
TextView txtView = (TextView) view.findViewById(R.id.message);
... the method could not find the textview because was using as ID, a resource from android.R instead of package.R.
The most strange is that along activities I made this time ago, sometimes I did the same mistake, but I could fix quickly because Eclipse said me that the resource specified wasnt found underlining the resource line, but for some reason, in this case didn't notify me. Maybe R.id.message is reserved for android.R.
I want to animated text to fade in and out when changing I am extending TextSwitcher in order to define this once since I have many TextSwitcher objects.
However I keep on getting a Null pointer "java.lang.NullPointerException
android.widget.TextSwitcher.setText(TextSwitcher.java:80) when doing this :
homeTeamTextView.setText("Text");
I decided to extend the TextSwitcher class like this:
public class MyTextSwitcher extends TextSwitcher{
public MyTextSwitcher(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
setInAnimation(context,R.anim.fade_in);
setOutAnimation(context,R.anim.fade_out);
}}
Activity
private MyTextSwitcher homeTeamTextView;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
homeTeamTextView = (MyTextSwitcher) findViewById(R.id.hometeam);
awayScoreTextView = (TextView) findViewById(R.id.awayteamscore);
homeScoreTextView = (TextView) findViewById(R.id.hometeamscore);
gameStatusTextView = (TextView) findViewById(R.id.gamestatus);
gameClockTextView = (TextView) findViewById(R.id.gameclock);
gameStartTextView = (TextView) findViewById(R.id.gamestart);
gameClockSectionTextView = (TextView) findViewById(R.id.gameclocksection);
LoadDataAsyncTask loadDataTask = new LoadDataAsyncTask();
loadDataTask.execute();
}
Layout:
<com.MyTextSwitcher
android:id="#+id/hometeam"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
It looks that TextSwitcher is common ViewSwitcher which can contain only TextViews. When you call setText() it try to get next view. You can find it in source: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/widget/TextSwitcher.java, but it's null. I think you must add textView inside your TextSwitcher (or even two textView to properly working animation).
I have an activity (MyActivity1) which starts a view class (MyView1) using setContentView();
In MyView1, after some drawings with canvas, I want to be able to return to MyActivity1. I tried MyView1.this.setVisibility(View.INVISIBLE) or View.GONE but these just make the screen blank.
I also tried ((Activity)getContext()).finish(); but it creates a runtime error.
I should say that the back button works fine and pressing it closes MyView1 and brings the activity back. However, I want to be able to do that programmatically inside the view. for example I want when user touches a specific part of the screen in the view class, it closes itself and brings back the parent activity. How should I implement this?
It seems a very simple task! but I could not find the answer after searching through the similar questions.
Here is the way MyActivity1 starts the view:
public class MyActivity1 extends Activity {
MyView1 View1;
public void Start_Button(View view)
{
Context ctx = getApplicationContext();
View1= new MyView1(ctx, null);
setContentView(View1);
}
}
and the part in MyView1 where I want to write something to close itself and bring MyActivity1 back:
public class MyView1 extends View {
static Context mycontext;
public MyView1(Context context, AttributeSet attrs) {
super(context, attrs);
mycontext=context;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
this.setVisibility(View.INVISIBLE);
((Activity) mycontext).setContentView(R.layout.activity_myactivity1);
// This gives runtime error
}
}
public public MyView1(Activity activity, AttributeSet attrs) {
super(context, attrs);
myActivity=activity;
}
public void Start_Button(View view)
{
View1= new MyView1(this, null);
setContentView(View1);
}
Just call myActivity.setContentView(R.layout.original_layout_for_MyActivity1);
I have a View that I would like a TextView to pop up over when an event occurs. I have done this before (a long time ago) but can't remember how I did it...
My code is for the View element. Upon adding the TextView, I would like it to show over the top of the View.
public class test extends View {
public test(Context context) {
super(context);
setBackgroundColor(Color.RED);
}
TextView tv;
public void adText(TextView tv){
this.tv =tv;
tv.setVisibility(tv.VISIBLE);
}
}
http://www.curious-creature.org/2009/03/01/android-layout-tricks-3-optimize-part-1/
Solves it. Someone delete that wierd comment please...