I linked the string array to a variable and the I have a button that will add index by 1 and set the textView to the string-array variable[index] so i can show the list of the item in string array one by one, but when I press the button, nothing happen and ViewPostIme0 and ViewPostIme pointer 1 keep cycling in my logcat
I tried everything and
I already tried to simplify the code to its shortest, but still don't work
Simplify code
int index = 0;
Button btn_message;
btn_message = findViewById(R.id.btn_message);
public void onClick(View v) {
switch (v.getId()) {
case (R.id.btn_message):
//if changes in the same page
index++;
btn_message.setText(String.valueOf(messages[index]));
}
}
Full code
package com.example.project;
import android.content.Intent;
import android.content.res.Resources;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btn_message, btn_skip;
String[] messages, personal_name;
ImageView LeftImage, RightImage, BackgroundImage;
String name;
TextView test, txt_round;
int index ;
int character;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Importing values
final Intent intent2 = getIntent();
index = intent2.getIntExtra("index", 0);
Intent intent_name = getIntent();
name = intent_name.getStringExtra("name");
//get string array
personal_name = getResources().getStringArray(R.array.name);
messages = getResources().getStringArray(R.array.text);
//find view by id
btn_message = findViewById(R.id.btn_message);
LeftImage = findViewById(R.id.img_left);
RightImage = findViewById(R.id.img_right);
BackgroundImage = findViewById(R.id.img_Background);
test = findViewById(R.id.txtx_Test);
btn_skip = findViewById(R.id.btn_skip);
txt_round = findViewById(R.id.txt_round);
String testing = String.valueOf(index);
test.setText(testing);
testing = String.valueOf(index);
test.setText(testing);
//set Value to varibles
//if image come to main, use this to change things
switch (index) {
case (200):
//BackgroundImage.setImageResource(R.mipmap.);
btn_message.setText(String.valueOf(messages[index]));
break;
}
/* case (13):
test.setText("success");
btn_message.setText(String.valueOf(messages[index]));
break;
case (20):
test.setText("Success2");
btn_message.setText(String.valueOf(messages[index]));
break;
}*/
//Intent intent = getIntent();
//index = intent.getIntExtra("index", 0);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case (R.id.btn_message):
//if changes in the same page
index++;
String testing = String.valueOf(index);
test.setText(testing);
btn_message.setText(String.valueOf(messages[index]));
switch (index) {
//CHANGING POINT ZONE
case (8):
index = 10;
//BackgroundImage.setImageResource(R.mipmap.bg1);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
txt_round.setText(name);
break;
case (16):
index = 20;
BackgroundImage.setImageResource(R.mipmap.bg2);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
txt_round.setText(name);
break;
case (31):
index = 40;
//BackgroundImage.setImageResource(R.mipmap.bg3);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
txt_round.setText("");
break;
case (52):
index = 60;
BackgroundImage.setImageResource(R.mipmap.bg4);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
txt_round.setText("");
break;
case (66):
index = 70;
//BackgroundImage.setImageResource(R.mipmap.cg2);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
txt_round.setText(name);
break;
case (82):
index = 90;
//BackgroundImage.setImageResource(R.mipmap.bg5);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
txt_round.setText("");
break;
case (172):
index = 180;
txt_round.setText(name);
BackgroundImage.setImageResource(R.mipmap.bg6);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
case (183):
index = 190;
txt_round.setText(name);
//BackgroundImage.setImageResource(R.mipmap.cg3);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
//END A END A END A
case (114):
index = 120;
txt_round.setText("");
//BackgroundImage.setImageResource(R.mipmap.bg10);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
case (126):
index = 130;
//BackgroundImage.setImageResource(R.mipmap.bg11);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
txt_round.setText(name);
break;
//END A END A END A
case (145):
//index = 90;
txt_round.setText("");
//BackgroundImage.setImageResource(R.mipmap.endA);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
//END A END A END A
//END B END B END B
case (211):
index = 220;
txt_round.setText(name);
//BackgroundImage.setImageResource(R.mipmap.bg6);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
case (224):
index = 230;
txt_round.setText(name);
//BackgroundImage.setImageResource(R.mipmap.bg4);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
case (234):
index = 240;
txt_round.setText(name);
//BackgroundImage.setImageResource(R.mipmap.cg6);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
//END B END B END B
case (251):
//index = 240;
txt_round.setText(name);
//BackgroundImage.setImageResource(R.mipmap.cg6);
BackgroundImage.setScaleType(ImageView.ScaleType.FIT_XY);
break;
//END B END B END B
//CHOICE AREA
case (105):
AlertDialog.Builder choice_box = new AlertDialog.Builder(MainActivity.this);
View choice_view = getLayoutInflater().inflate(R.layout.activity_choices, null);
Button choice1 = choice_view.findViewById(R.id.choice1);
Button choice2 = choice_view.findViewById(R.id.choice2);
choice_box.setView(choice_view);
AlertDialog dialog = choice_box.create();
dialog.setCanceledOnTouchOutside(false);
final AlertDialog finalDialog3 = dialog;
choice1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = 110;
btn_message.setText(String.valueOf(messages[index]));
txt_round.setText("");
finalDialog3.dismiss();
}
});
//if in screen choice, use this to change in different paths
final AlertDialog finalDialog2 = dialog;
choice2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = 150;
//need to set the index 20's value as instant refresting is not working
btn_message.setText(String.valueOf(messages[index]));
txt_round.setText("");
finalDialog2.dismiss();
}
});
dialog.show();
Window window = dialog.getWindow();
window.setLayout(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.WRAP_CONTENT);
break;
case (203):
choice_box = new AlertDialog.Builder(MainActivity.this);
choice_view = getLayoutInflater().inflate(R.layout.activity_choices, null);
choice1 = choice_view.findViewById(R.id.choice1);
choice2 = choice_view.findViewById(R.id.choice2);
dialog = choice_box.create();
final AlertDialog finalDialog = dialog;
choice1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = 210;
btn_message.setText(String.valueOf(messages[index]));
txt_round.setText("");
finalDialog.dismiss();
}
});
//if in screen choice, use this to change in different paths
final AlertDialog finalDialog1 = dialog;
choice2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = 260;
//need to set the index 20's value as instant refresting is not working
btn_message.setText(String.valueOf(messages[index]));
txt_round.setText("");
finalDialog1.dismiss();
}
});
dialog.show();
window = dialog.getWindow();
window.setLayout(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.WRAP_CONTENT);
break;
case (264):
choice_box = new AlertDialog.Builder(MainActivity.this);
choice_view = getLayoutInflater().inflate(R.layout.activity_choices, null);
choice1 = choice_view.findViewById(R.id.choice1);
choice2 = choice_view.findViewById(R.id.choice2);
dialog = choice_box.create();
final AlertDialog finalDialog4 = dialog;
choice1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = 210;
btn_message.setText(String.valueOf(messages[index]));
txt_round.setText("");
finalDialog4.dismiss();
}
});
//if in screen choice, use this to change in different paths
final AlertDialog finalDialog5 = dialog;
choice2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = 260;
//need to set the index 20's value as instant refresting is not working
btn_message.setText(String.valueOf(messages[index]));
txt_round.setText("");
finalDialog5.dismiss();
}
});
dialog.show();
window = dialog.getWindow();
window.setLayout(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.WRAP_CONTENT);
break;
//CHARCTER ZONE
case (23):
case (29):
case (41):
case (49):
case (51):
case (78):
case (91):
case (93):
case (103):
case (111):
case (124):
case (134):
case (158):
case (161):
case (166):
case (169): {//girl
txt_round.setText("B");
break;
}
case (63):
case (72):
case (74):
case (152):
case (155):
case (157):
case (160):
case (163):
case (188):
case (171): {//shing
character = 2;
txt_round.setText("A");
break;
}
case (10):
case (20):
case (25):
case (30):
case (70):
case (75):
case (76):
case (79):
case (94):
case (100):
case (104):
case (112):
case (123):
case (130):
case (132):
case (136):
case (139):
case (143):
case (153):
case (159):
case (164):
case (180):
case (191): {//playername
txt_round.setText(name);
break;
}
case (193):
Intent intent = new Intent(this, ImageActivity.class);
intent.putExtra("index", index);
startActivity(intent);
break;
default://noone
character = 0;
txt_round.setText("");
//MINIGAME AREA
//OTHER ZONE (PLAYER NAME SET DIALOG ZONE)
btn_message.setText(String.valueOf(messages[index]));
break;
case R.id.btn_skip:
break;
}
}
}
}
Here is the main .xml file is really needed
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="#+id/img_Background"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="#mipmap/bg_test" />
<ImageView
android:id="#+id/img_right"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="450dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#mipmap/normal" />
<ImageView
android:id="#+id/img_left"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="450dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#mipmap/normal6" />
<Button
android:id="#+id/btn_message"
android:layout_width="713dp"
android:layout_height="146dp"
android:background="#mipmap/bar_5"
android:gravity="top"
android:lines="5"
android:minLines="2"
android:paddingLeft="80px"
android:paddingTop="50px"
android:text="「......enq7f…….」"
android:textAlignment="viewStart"
android:textSize="26sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/txtx_Test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
android:textColor="#color/colorPrimaryDark"
android:textSize="36sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/img_Background"
/>
<TextView
android:id="#+id/txt_round"
android:layout_width="171dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="590dp"
android:background="#mipmap/bar_6"
android:gravity="center"
android:paddingTop="15px"
android:text="\u003F \u003F \u003F"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="#+id/btn_message"
app:layout_constraintEnd_toEndOf="#+id/img_right"
app:layout_constraintHorizontal_bias="0.012"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/btn_skip"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginStart="600dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="3dp"
android:background="#mipmap/bar_3"
android:text="Skip"
app:layout_constraintBottom_toTopOf="#+id/btn_message"
app:layout_constraintEnd_toEndOf="#+id/img_right"
app:layout_constraintHorizontal_bias="0.012"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
Expected: The messages should be set on the button one by one,
Actual: but nothing happened
The reason is because you miss to set listener to your button.
btn_message.setOnClickListener(this);
As #John Joe mentioned you miss to set listener to your button if you don't need to implement listener add android:onClick="onClick" in your button at xml
<Button
android:id="#+id/btn_message"
android:layout_width="713dp"
android:layout_height="146dp"
android:background="#mipmap/bar_5"
android:gravity="top"
android:lines="5"
android:onClick="onClick"
android:minLines="2"
android:paddingLeft="80px"
android:paddingTop="50px"
android:text="「......enq7f…….」"
android:textAlignment="viewStart"
android:textSize="26sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Related
I have 4 items in the menu and 1 button Rec / Stop. I want, when the Rec button is active and records, the other 4 items in the menu items are disabled. Please help me.
this is activity_main.xml
<ToggleButton
android:id="#+id/recStop"
android:layout_width="65dp"
android:layout_height="65dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="100dp"
android:background="#drawable/tbutton"
android:text=""
android:textOff=""
android:textOn="" />
this is tbutton.xml
<item android:drawable="#drawable/rec"
android:state_checked="false" />
<item android:drawable="#drawable/stop"
android:state_checked="true" />
this is MainActivity.java
private ToggleButton toggleButton;
toggleButton = (ToggleButton) findViewById(R.id.recStop);
// Button Rec / Stop
toggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (isChecked) {
speech.setRecognitionListener(VoiceRecognitionActivity.this);
progressBar.setVisibility(View.VISIBLE);
progressBar.setIndeterminate(true);
speech.startListening(recognizerIntent);
} else {
progressBar.setIndeterminate(false);
progressBar.setVisibility(View.INVISIBLE);
speech.stopListening();
speech.destroy();
}
}
});
this is MainActivity.java
// Menu items
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()) {
case R.id.copy:
break;
}
switch (item.getItemId()) {
case R.id.share:
break;
}
switch (item.getItemId()) {
case R.id.clear:
break;
}
switch (item.getItemId()) {
case R.id.about:
break;
}
return super.onOptionsItemSelected(item);
}
Inside each condition of your switch...case, check the status of togglebutton and if its checked avoid further actions in it. Also don't use multiple switch, you must define multiple cases inside it see the code below.
switch (item.getItemId()) {
case R.id.copy:
if(toggleButton.isChecked()) {
//display warning message
} else {
// your regular code here
}
break;
case R.id.share:
if(toggleButton.isChecked()) {
//display warning message
} else {
// your regular code here
}
break;
case R.id.clear:
if(toggleButton.isChecked()) {
//display warning message
} else {
// your regular code here
}
break;
case R.id.about:
if(toggleButton.isChecked()) {
//display warning message
} else {
// your regular code here
}
break;
}
You can achieve that with this code
switch (item.getItemId()) {
case R.id.copy:
if(toggleButton.isChecked()) {
menu.findItem(R.id.copy).setEnabled(false);
} else {
menu.findItem(R.id.copy).setEnabled(true);
}
break;
//you do the same for the rest of menu buttons
}
I am working on an Android calculator app not using any of the built in timer based classes in Android, but instead using Handlers and Threads to update the UI. I'm not sure if there is a problem with my logic or not, but for whatever reason when I set a time and hit the Start button, nothing happens on the screen at all. The targeted TextView does not decrease as it should. Again, I may have made a simple errors (or a few), but I am posting my java and xml files for you all to look at. Thanks in advance for any responses.
TimerActivity.java
package com.example.stins.intentsandtimer;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.NumberPicker;
import android.widget.TextView;
import android.os.Handler;
import android.os.Message;
import android.content.Context;
import android.os.Vibrator;
public class TimerActivity extends AppCompatActivity implements View.OnClickListener {
TextView hours, minutes, seconds;
Button numberPicker;
private int hrs, min, sec;
private boolean start;
Handler timerHandler = new Handler(){
/**
* Handler for the timer class. It receives the onStart runnable to allow the textviews
* to be updated. It checks to see if all textviews are empty and only updates them if
* they follow the conditions of a traditional timer. Including moving from 1 hour to 59 minutes.
* The handler also sends the Vibrator function once the timer is complete.
* #param msg
*/
#Override
public void handleMessage(Message msg){
super.handleMessage(msg);
TextView txtSeconds = (TextView) findViewById(R.id.textview_seconds);
TextView txtMinutes = (TextView) findViewById(R.id.textview_minutes);
TextView txtHours = (TextView) findViewById(R.id.textview_hours);
int zeroCheck = Integer.parseInt(txtSeconds.getText().toString());
if (zeroCheck > 0) {
sec -= 1;
txtSeconds.setText(sec + "");
} else if (min > 0 && sec == 0) {
min -= 1;
txtMinutes.setText(min + "");
sec = 59;
txtSeconds.setText(sec + "");
} else if (hrs > 0 && min == 0 && sec == 0) {
hrs -= 1;
txtHours.setText(hrs + "");
min = 59;
txtMinutes.setText(min + "");
sec = 59;
txtSeconds.setText(sec + "");
} else {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(1000);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
this.setTitle("Timer");
Button btnStart = (Button) findViewById(R.id.start_button);
Button btnStop = (Button) findViewById(R.id.stop_button);
Button btnReset = (Button) findViewById(R.id.reset_button);
hours = (TextView) findViewById(R.id.textview_hours);
numberPicker = (Button) findViewById(R.id.btn_set_hours);
numberPicker.setOnClickListener(this);
minutes = (TextView) findViewById(R.id.textview_minutes);
numberPicker = (Button) findViewById(R.id.btn_set_minutes);
numberPicker.setOnClickListener(this);
seconds = (TextView) findViewById(R.id.textview_seconds);
numberPicker = (Button) findViewById(R.id.btn_set_seconds);
numberPicker.setOnClickListener(this);
btnReset.setOnClickListener(new Button.OnClickListener() {
public void onClick(View view) {
TextView txtSeconds = (TextView) findViewById(R.id.textview_seconds);
TextView txtMinutes = (TextView) findViewById(R.id.textview_minutes);
TextView txtHours = (TextView) findViewById(R.id.textview_hours);
sec = 0;
min = 0;
hrs = 0;
txtSeconds.setText(sec+"");
txtMinutes.setText(min+"");
txtHours.setText(hrs+"");
}
}
);
btnStart.setOnClickListener(new Button.OnClickListener() {
public void onClick(View view) {
start = true;
onStart();
}
}
);
btnStop.setOnClickListener(new Button.OnClickListener() {
public void onClick(View view) {
start = false;
}
}
);
}
protected void onStart(){
super.onStart();
final Thread myThread = new Thread(new Runnable(){
#Override
public void run() {
while (sec > 0 || min > 0 || hrs > 0) {
if(start) {
try {
Thread.sleep(1000);
timerHandler.sendMessage(timerHandler.obtainMessage());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
}
}
}
});
myThread.start();
}
public void onClick (View v){
switch (v.getId()) {
case R.id.btn_set_hours:
hourPickerDialog();
break;
case R.id.btn_set_minutes:
minutePickerDialog();
break;
case R.id.btn_set_seconds:
secondPickerDialog();
break;
default:
break;
}
}
private void hourPickerDialog(){
NumberPicker myNumberPicker = new NumberPicker(this);
myNumberPicker.setMaxValue(99);
myNumberPicker.setMinValue(0);
NumberPicker.OnValueChangeListener myValChangedListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
hours.setText(""+newVal);
}
};
myNumberPicker.setOnValueChangedListener(myValChangedListener);
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(myNumberPicker);
builder.setTitle("Set Hours");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
private void minutePickerDialog(){
NumberPicker myNumberPicker = new NumberPicker(this);
myNumberPicker.setMaxValue(59);
myNumberPicker.setMinValue(0);
NumberPicker.OnValueChangeListener myValChangedListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
minutes.setText(""+newVal);
}
};
myNumberPicker.setOnValueChangedListener(myValChangedListener);
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(myNumberPicker);
builder.setTitle("Set Minutes");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
private void secondPickerDialog(){
NumberPicker myNumberPicker = new NumberPicker(this);
myNumberPicker.setMaxValue(59);
myNumberPicker.setMinValue(0);
NumberPicker.OnValueChangeListener myValChangedListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
seconds.setText(""+newVal);
}
};
myNumberPicker.setOnValueChangedListener(myValChangedListener);
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(myNumberPicker);
builder.setTitle("Set Seconds");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.show();
}
}
activity_timer.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:id="#+id/textview_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="00"
android:textSize="70sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text=":"
android:textSize="70sp"/>
<TextView
android:id="#+id/textview_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="00"
android:textSize="70sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text=":"
android:textSize="70sp"/>
<TextView
android:id="#+id/textview_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="00"
android:textSize="70sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="#+id/btn_set_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hours"/>
<Button
android:id="#+id/btn_set_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Minutes"/>
<Button
android:id="#+id/btn_set_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Seconds"/>
</LinearLayout>
<Button
android:id="#+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:background="?selectableItemBackgroundBorderless"
android:text="#string/timer_start"
style="#style/MyButton"
/>
<Button
android:id="#+id/stop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:background="?selectableItemBackgroundBorderless"
android:text="#string/timer_stop"
style="#style/MyButton"/>
<Button
android:id="#+id/reset_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="?selectableItemBackgroundBorderless"
android:text="#string/timer_reset"
style="#style/MyButton"/>
</LinearLayout>
There's several things going on in your code. I won't try to address them all but just some to get your code doing about what it should. I've copied & tried your code & it actually changes the display for me. I skipped your time picker dialogs & just set sec=20 to start. If you're not getting any changing display, is the display being set initially from the time pickers?
Anyway, 1st let's talk about debugging. One way to do this is to put Log statements in your code. Start by putting this at the top of the file
private final static String TAG = "TimerActivity";
Then in the code have things like this:
// put this in the start button click listener
Log.d(TAG, "Start clicked");
// or this in handleMessage
Log.d(TAG, "handleMessage(), seconds = " + sec);
Having these Log message can help you know what your program has done & what it hasn't, plus show you some variable values. You could also use the debugger which I won't get into now.
Now for your code. onStart() is a lifecycle method. You should not call it yourself. Rename your method (maybe something like onStartButton()). As you have it now, you have 2 instances of your thread running and your counter goes down twice in each second.
In handleMessage(), you have variables (hrs, min, sec) that you use to track the time but you also have zeroCheck that you read from the text on the display. The better thing to do would be use the variables you're already keeping anyway (if(sec > 0) { sec -= 1;...). I didn't verify your logic in the rest of these conditions. Once the display is updating, I'll leave that for you.
Lastly, txtSeconds.setText(sec + ""); is not a good way to use setText() (it's probably OK for Log messages but it's better to get accustomed to using text in other ways). There is more than 1 good way to display text but for this instance, you need special formatting. That is you want your display to show a leading 0 for each number "00:09:07" not "0:9:7". You can get that with
txtSeconds.setText(String.format("%02d", sec));
This way always gives a 2 digit display, from 0 to 59. Other useful formatters are "%08x" for 32 bit hexadecimal or "%.2f" which limits display to 2 places past the decimal place like for showing dollars and cents.
So, none of these will fix the problem in your post but they will get your final code closer to what it needs to be. As I said, your code updates the display as it is for me (not using the time pickers). You can start by setting sec to a fixed number then hit the "Start" button to see what happens. If there are problems in your time pickers, you can use Log messages to track down the bugs & fix them.
EDIT:
So what's happening with your timer not starting is that, while you change the display in your number picker, you don't set the underlying variables (sec etc.) Define some variables to use as temp storage (temp_sec etc.) then set this in onValueChange(),
temp_sec = newVal;
Now in your positiveButton onClick(), you'll have
sec = temp_sec;
So, I have a viewflipper which is filled with several webviews. I've then extended the WebView class in order to catch the motions for the viewflipper.
The problem is that when I swipe, the app crashes and gives me the following error:
10-09 17:23:14.443: E/MessageQueue-JNI(21126): java.lang.NullPointerException: Attempt to invoke virtual method 'int android.widget.ViewFlipper.getDisplayedChild()' on a null object reference
10-09 17:23:14.443: E/MessageQueue-JNI(21126): at com.test.hamnarbetare.CustomWebView.onTouchEvent(CustomWebView.java:55)
The code can be viewed here:
package com.test.hamnarbetare;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.webkit.WebView;
import android.widget.ViewFlipper;
public class CustomWebView extends WebView {
private float lastX;
private ViewFlipper viewFlipper;
float downXValue;
long downTime;
private float lastTouchX, lastTouchY;
private boolean hasMoved = false;
public CustomWebView(Context context) {
super(context);
viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
}
public boolean onTouchEvent(MotionEvent evt) {
boolean consumed = super.onTouchEvent(evt);
if (isClickable()) {
switch (evt.getAction()) {
case MotionEvent.ACTION_DOWN:
lastTouchX = evt.getX();
lastTouchY = evt.getY();
downXValue = evt.getX();
downTime = evt.getEventTime();
hasMoved = false;
break;
case MotionEvent.ACTION_MOVE:
hasMoved = moved(evt);
break;
case MotionEvent.ACTION_UP:
float currentX = evt.getX();
long currentTime = evt.getEventTime();
float difference = Math.abs(downXValue - currentX);
long time = currentTime - downTime;
if ( (downXValue < currentX) && (time < 220) && (difference > 100) ) {
if (viewFlipper.getDisplayedChild() == 1)
break;
viewFlipper.setInAnimation(getContext(), R.anim.slide_in_from_right); // Next screen comes in from right.
viewFlipper.setOutAnimation(getContext(), R.anim.slide_out_to_left); // Current screen goes out from left.
// Display previous screen.
viewFlipper.showPrevious();
}
if ( (downXValue > currentX) && (time < 220) && (difference > 100) ) {
if (viewFlipper.getDisplayedChild() == 0)
break;
viewFlipper.setInAnimation(getContext(), R.anim.slide_in_from_left); // Next screen comes in from left.
viewFlipper.setOutAnimation(getContext(), R.anim.slide_out_to_right); // Current screen goes out from right.
// Display next screen.
viewFlipper.showNext();
}
//if (!moved(evt)) performClick();
break;
}
}
return consumed || isClickable();
}
private boolean moved(MotionEvent evt) {
return hasMoved || Math.abs(evt.getX() - lastTouchX) > 10.0 || Math.abs(evt.getY() - lastTouchY) > 10.0;
}
/*public boolean onTouchEvent(MotionEvent touchevent) {
switch (touchevent.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = touchevent.getX();
break;
case MotionEvent.ACTION_UP:
float currentX = touchevent.getX();
// Handling left to right screen swap.
if (lastX < currentX) {
// If there aren't any other children, just break.
if (viewFlipper.getDisplayedChild() == 0)
break;
viewFlipper.setInAnimation(getContext(), R.anim.slide_in_from_left); // Next screen comes in from left.
viewFlipper.setOutAnimation(getContext(), R.anim.slide_out_to_right); // Current screen goes out from right.
// Display next screen.
viewFlipper.showNext();
}
// Handling right to left screen swap.
if (lastX > currentX) {
// If there is a child (to the left), just break.
if (viewFlipper.getDisplayedChild() == 1)
break;
viewFlipper.setInAnimation(getContext(), R.anim.slide_in_from_right); // Next screen comes in from right.
viewFlipper.setOutAnimation(getContext(), R.anim.slide_out_to_left); // Current screen goes out from left.
// Display previous screen.
viewFlipper.showPrevious();
}
break;
}
return false;
}*/
}
For some reason, viewFlipper is unknown at this point. HOWEVER if I replace the onTouchEvent with the previous onTouchEvent which is located at the bottom, it all works fine and I can't understand why! Any ideas?
EDIT: Layout xml file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:paddingTop="10dp" >
<ViewFlipper
android:id="#+id/viewFlipper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.70" />
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/actionbar_bg" >
<Button
android:id="#+id/btn_left"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="0.23"
android:text="#string/previous" />
<TextView
android:id="#+id/page_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.23"
android:gravity="center"
android:text=""
android:textColor="#FFFFFF" />
<Button
android:id="#+id/btn_right"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="0.35"
android:text="#string/next" />
</LinearLayout>
</LinearLayout>
The problem is that CustomWebView can't find R.id.viewFlipper with findViewById.
What you need to do is lookup R.id.viewFlipper in the ParentActivity and pass the ViewFlipper in the CustomWebView constructor.
Example code below, not compiled/tested.
CustomWebView.java
public CustomWebView(Context context, ViewFlipper viewFlipper) {
super(context);
this.viewFlipper = viewFlipper;
}
ParentActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout_xml);
ViewFlipper viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
CustomWebView webView = new CustomWebView(this, viewFlipper);
// TODO: Add webView to viewFlipper
}
I have a few ImageViews, all using the same onClick(). How can I get the ID or R.drawable int from the specific ImageView to open another Image depending on the Image I clicked on?
I imagine you have some image views in your xml layout file:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_content" >
<ImageView
android:id="#+id/image_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/image_drawable_1"
android:onClick="doSomething" />
<ImageView
android:id="#+id/image_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/image_drawable_2"
android:onClick="doSomething" />
<ImageView
android:id="#+id/image_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/image_drawable_3"
android:onClick="doSomething" />
</LinearLayout>
You have to add this method to your Activity:
public void doSomething(View v) {
switch (v.getId()) {
case R.id.image_1:
// You clicked on image 1
// If you need to do something to the ImageView with this particular Id then uncomment the next line.
// ImageView imageView = (ImageView) v;
break;
case R.id.image_2:
// You clicked on image 2
break;
case R.id.image_3:
// You clicked on image 3
break;
}
}
Use getResources().getDrawable
Example:
ImageView myImg = (ImageView) findViewById(R.id.myImg);
myImg.setImageResource = getResources().getDrawable( R.drawable.icon );
I don't think you can get the resource identifier that was used to generate the Drawable for the ImageView, but you can save it as a tag of the ImageView when you set the image programmatically.
imageView.setImageResource(R.drawable.image_resource);
imageView.setTag(R.drawable.image_resource);
You can than later retrieve it like that:
int imageResource = (Integer) imageView.getTag();
I stored the Ids of the images which are shown by clicking the onClick() in an int-array, so I created something like a workaround to deal with that issue.
My onClick() looks like this now:
public void showPictureZoomDialog(View view)
{
int singlePicValue400 = 0;
switch (view.getId())
{
case R.id.picture_imgvw_pic1:
singlePicValue400 = currentPicSite.getPicValues400()[0];
break;
case R.id.picture_imgvw_pic2:
singlePicValue400 = currentPicSite.getPicValues400()[1];
break;
case R.id.picture_imgvw_pic3:
singlePicValue400 = currentPicSite.getPicValues400()[2];
break;
case R.id.picture_imgvw_pic4:
singlePicValue400 = currentPicSite.getPicValues400()[3];
break;
case R.id.picture_imgvw_pic5:
singlePicValue400 = currentPicSite.getPicValues400()[4];
break;
case R.id.picture_imgvw_pic6:
singlePicValue400 = currentPicSite.getPicValues400()[5];
break;
case R.id.picture_imgvw_pic7:
singlePicValue400 = currentPicSite.getPicValues400()[6];
break;
case R.id.picture_imgvw_pic8:
singlePicValue400 = currentPicSite.getPicValues400()[7];
break;
case R.id.picture_imgvw_pic9:
singlePicValue400 = currentPicSite.getPicValues400()[8];
break;
case R.id.picture_imgvw_pic10:
singlePicValue400 = currentPicSite.getPicValues400()[9];
break;
}
Intent intent = new Intent(this, PictureZoomDialog.class);
intent.putExtra("PICID", singlePicValue400);
startActivity(intent);
}
I get the Id of the image I want to display via the Intent:
public class PictureZoomDialog extends Activity {
private ImageView mDialog;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture_zoom_dialog);
mDialog = (ImageView)findViewById(R.id.pictureactivity_imgvw_pic1);
mDialog.setClickable(true);
mDialog.setImageResource(getIntent().getIntExtra("PICID",-1));
mDialog.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
}
Thx a lot guys for getting me in the right direction!
You can use the following to get the current Drawable from the ImageView.
Drawable myDrawable = YourImageView.getDrawable();
Then you can go ahead and use myDrawable where ever you want.
I'd like to have a TextView display text, and when you click/longclick on it, a textbox should "show up" and allow editing of said text. When you're done editing (onkey enter i suppose) it should revert back to a textview with the updated text...
I'm wondering if it's feasable to implement such a widget or should I hack a workaround? Tips and suggestions are very welcome.
If you need further idea of what I mean, just go to your e.g. (windows) skype profile and see for yourself.
EDIT:
Clarification: I'm specifically asking for a widget or such which is a textview until clicked on, then transforms to an edittext containing the same text; once done editing it transforms back to a textview representing the new changed text. Thats what i mean by "edittext on demand widget".
But I'm hoping to get something better than
public class Widget {
TextView text;
EditText edit;
String textToRepresent;
//...
}
You have a few different options here.
First you will have to register an onClick or onLongClick to the TextView that you want to make interactive. Just make sure that the user knows it's clickable
Then have your onClick function start a DialogFragment. I like to create show functions. Note that you can use the support libraries here to make your app backwards compatible.
private void showDialog() {
MyDialogFragment dialog = new MyDialogFragment();
dialog.show(getSupportFragmentManager(), "dialog");
}
The DialogFragment is pretty straight forward. In your onCreateView you'll inflate the View that you'll want to display to the user. You can alternatively wrap it with a simple AlertDialogBuilder if you don't want to go custom.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.your_dialog_layout);
mTitleEditText = (TextView) view.findViewById(R.id.title);
mTitleEditText.setOnClickListener(this);
return view;
}
After your findViewByIds set your onClickListeners.
The last thing you have to take care of is getting data back into your original TextView.
You can do this by creating a public method in your Activity that you can call from inside of your DialogFragment. Something like this
#Override
public void onClick(View v) {
int clickedId = v.getId();
if (clickedId == mDoneButton.getId()) {
MyActivity activity = (MyActivity)getActivity();
mTitle = mTitleEditText.getText().toString();
activity.setText(mTitle);
dismiss();
}
}
I would recommend using a DialogFragment because it will handle your life cycle nicely.
However, another option would be to create a new Activity themed to be a dialog
<activity android:theme="#android:style/Theme.Dialog" />
Then you can startActivityForResult to display your dialog and then capture your results in onActivityResult
Here is my solution. I just give you the basic one. Create a TextView in front of EditText and two Button OK,Cancel (You can change to ImageButton like Skype). Change the visiblity of two view. The code is so simple without comment. You can add some null checking according your logic.
public class CompoundTextView extends RelativeLayout implements OnClickListener {
private EditText edt;
private TextView txt;
RelativeLayout layout;
public SkypeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
edt = (EditText) findViewById(R.id.edt);
txt = (TextView) findViewById(R.id.txt_name);
layout = (RelativeLayout) findViewById(R.id.layout);
Button ok = (Button) findViewById(R.id.ok_btn);
Button cancel = (Button) findViewById(R.id.cancel_btn);
ok.setOnClickListener(this);
cancel.setOnClickListener(this);
txt.setOnClickListener(this);
}
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.ok_btn:
String editString = edt.getText().toString();
txt.setText(editString);
layout.setVisibility(View.INVISIBLE);
txt.setVisibility(View.VISIBLE);
break;
case R.id.cancel_btn:
layout.setVisibility(View.INVISIBLE);
txt.setVisibility(View.VISIBLE);
break;
case R.id.txt_name:
txt.setVisibility(View.INVISIBLE);
layout.setVisibility(View.VISIBLE);
break;
}
}
}
Create a XML skypetextview. You can customize font and background to make it's prettier.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="#+id/txt_name"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:textColor="#FFFFFF"
android:textSize="14sp"
android:background="#ff0000" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:id="#+id/layout" >
<EditText
android:id="#+id/edt"
android:layout_width="270dp"
android:layout_height="100dp" />
<Button
android:id="#+id/ok_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/edt"
android:text="OK" />
<Button
android:id="#+id/cancel_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/ok_btn"
android:layout_toRightOf="#id/edt"
android:text="Cancel" />
</RelativeLayout>
</RelativeLayout>
add (or include) this view to the layout you want.
Example :
public class TestActivity extends Activity {
SkypeTextView test;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflate = getLayoutInflater();
test = (SkypeTextView ) inflate.inflate(R.layout.compound_text_view,
null);
setContentView(test);
}
PS: i forgot. You should add some underline format for your textview in order to make user notice it clickable
Let a EditText change its background based on its state(Editable or Frozen). Set a background selector that does this.
Use this selector xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_focused="true" android:drawable="#android:drawable/edit_text"/>
<item android:drawable="#android:drawable/screen_background_light_transparent"/>
</selector>
Like I said on thursday... Yul was pretty close but not quite close. He did have a general same idea but (theoretically) rushed into code too early ;)
The TextBoxOnDemand code supplied below is production-ready. The idea is similar to what I wanted to avoid in the OP and what Yul suggested, but with optimal implementation (using a ViewSwitcher instead of a RelativeLayout for instance)
I gathered the resources needed for this in the following articles:
Creating custom view from xml
Declaring a custom android UI element using XML
Defining custom attrs
How to pass custom component parameters in java and xml
http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/
and decided to post them here because the official Google "training" docs are useless and are either obsolete (deprecated) or do not cover what I needed. I hope you don't mind me claiming my own bounty, but this is the solution I wanted (and expected, ergo the bounty).
I guess the code will have to do ;)
TextBoxOnDemand.java:
package com.skype.widget;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnHoverListener;
import android.view.View.OnLongClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.ViewSwitcher;
import com.skype.ref.R;
import com.skype.ref.RemoteKeys;
public class TextBoxOnDemand extends ViewSwitcher implements OnClickListener, OnLongClickListener, OnFocusChangeListener, OnHoverListener,
OnEditorActionListener
{
public static final String LOGTAG = "TextBoxOnDemand";
private View btmGuard;
private ImageButton cancel, accept;
private EditText editor;
private RelativeLayout editorLayout;
private TextView face;
private String hint = new String();
private boolean inEditMode = false; //normally this is in textview mode
private boolean inputReady = false;
private String ourData = new String();
private String prefillData = new String();
private String tag = new String(); //usually tag is empty.
private View topGuard;
private int autoLinkMask;// = Linkify.EMAIL_ADDRESSES; //Linkify.ALL;
private ColorStateList textColor, hintColor = null;
public TextBoxOnDemand(Context context)
{
super(context);
build(context);
setEditable(false); //init
}
public TextBoxOnDemand(Context context, AttributeSet attrs)
{
super(context, attrs);
build(context);
init(context, attrs);
setEditable(false); //init
}
public String getPrefillData()
{
return prefillData;
}
public String getTag()
{
return tag;
}
public String getText()
{
Log.d(LOGTAG, "getText() returning '" + ourData + "'");
return ourData;
}
public boolean hasPrefillData()
{
return prefillData.isEmpty();
}
public boolean isEditable()
{
Log.d(LOGTAG, "isEditable() returning " + inEditMode);
return inEditMode;
}
#Override
public void onClick(View v)
{
Log.d(LOGTAG, "onClick(" + v + ")");
if (inEditMode)
{
if (v.equals(accept))
{
if (editor.getEditableText().length() == 0 || editor.getEditableText().length() > 5)
ourData = editor.getEditableText().toString();
setEditable(false);
} else if (v.equals(cancel))
{
setEditable(false);
}
}
}
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
// Log.d(LOGTAG, "onEditorAction(" + v + ", " + actionId + ", " + event + ") fired!");
Log.d(LOGTAG, "onEditorAction() fired, inputReady = " + inputReady);
if (editor.getEditableText().length() > 0 && editor.getEditableText().length() < (prefillData.length() + 2)) return true; //the user needs to enter something
if (inputReady && (event.getKeyCode() == RemoteKeys.ENTER.keycode() || event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) //always is
{
if (editor.getEditableText().length() > prefillData.length() || editor.getEditableText().length() == 0)
ourData = editor.getEditableText().toString();
setEditable(false);
return false;
}
if ((editor.getEditableText().toString().compareToIgnoreCase(ourData) == 0 || editor.getEditableText().toString()
.compareToIgnoreCase(prefillData) == 0)
&& !inputReady) //means we didn't just keep on holding enter
return true;
else
inputReady = true;
return true;
}
#Override
public void onFocusChange(View v, boolean hasFocus)
{
Log.d(LOGTAG, "onFocusChange(" + v + ", " + hasFocus + ")\tinEditMode = " + inEditMode);
if (inEditMode)
{
if (hasFocus && (v.equals(topGuard) || v.equals(btmGuard)))
{
setEditable(false);
requestFocus();
}
if (hasFocus && (v.equals(editor) || v.equals(accept) || v.equals(cancel)))
{
//do nothing, you should be able to browse freely here
if (ourData.isEmpty() && editor.getEditableText().length() < prefillData.length())
{
Log.d(LOGTAG, "adding prefill, before = " + editor.getEditableText());
editor.setText("");
editor.append(prefillData);
Log.d(LOGTAG, "now is = " + editor.getEditableText());
}
}
} else
{
String text = (ourData.isEmpty()) ? hint : ourData;
ColorStateList color;
if (hintColor != null && ourData.isEmpty())
color = hintColor;
else
color = textColor;
face.setTextColor(color);
if (hasFocus)
{
SpannableString ss = new SpannableString(text);
ss.setSpan(new UnderlineSpan(), 0, text.length(), 0);
face.setText(ss);
} else
face.setText(text);
}
}
#Override
public boolean onHover(View v, MotionEvent event)
{
// Log.d(LOGTAG, "onHover()");
String text = (ourData.isEmpty()) ? hint : ourData;
ColorStateList color;
if (hintColor != null && ourData.isEmpty())
color = hintColor;
else
color = textColor;
face.setTextColor(color);
switch (event.getAction())
{
case MotionEvent.ACTION_HOVER_ENTER:
SpannableString ss = new SpannableString(text);
ss.setSpan(new UnderlineSpan(), 0, text.length(), 0);
face.setText(ss);
break;
case MotionEvent.ACTION_HOVER_EXIT:
face.setText(text);
break;
}
return true;
}
#Override
public boolean onLongClick(View v)
{
Log.d(LOGTAG, "onLongClick()\tinEditMode = " + inEditMode);
if (!inEditMode) //implies that getDisplayedChild() == 0, meaning the textview
{
setEditable(true);
return true;
} else
return false;
}
public void setEditable(boolean value)
{
Log.d(LOGTAG, "setEditable(" + value + ")");
inEditMode = value;
if (inEditMode)
{
//display the editorLayout
face.setOnLongClickListener(null);
face.setOnHoverListener(null);
face.setOnFocusChangeListener(null); //because of GC.
face.setOnClickListener(null);
face.setVisibility(View.GONE);
setDisplayedChild(1);
editorLayout.setVisibility(View.VISIBLE);
editor.setOnFocusChangeListener(this);
editor.setOnEditorActionListener(this);
cancel.setOnClickListener(this);
accept.setOnClickListener(this);
accept.setOnFocusChangeListener(this);
cancel.setOnFocusChangeListener(this);
} else
{
editor.setOnFocusChangeListener(null);
editor.setOnEditorActionListener(null);
cancel.setOnClickListener(null);
accept.setOnClickListener(null);
accept.setOnFocusChangeListener(null);
cancel.setOnFocusChangeListener(null);
editorLayout.setVisibility(View.GONE);
setDisplayedChild(0);
face.setVisibility(View.VISIBLE);
face.setOnLongClickListener(this);
face.setOnHoverListener(this);
face.setOnFocusChangeListener(this);
face.setOnClickListener(this);
face.setFocusable(true);
face.setFocusableInTouchMode(true);
}
updateViews();
}
#Override
public void setNextFocusDownId(int nextFocusDownId)
{
super.setNextFocusDownId(nextFocusDownId);
face.setNextFocusDownId(nextFocusDownId);
// editor.setNextFocusDownId(nextFocusDownId);
accept.setNextFocusDownId(nextFocusDownId);
cancel.setNextFocusDownId(nextFocusDownId);
}
#Override
public void setNextFocusForwardId(int nextFocusForwardId)
{
super.setNextFocusForwardId(nextFocusForwardId);
face.setNextFocusForwardId(nextFocusForwardId);
editor.setNextFocusForwardId(nextFocusForwardId);
}
#Override
public void setNextFocusLeftId(int nextFocusLeftId)
{
super.setNextFocusLeftId(nextFocusLeftId);
face.setNextFocusLeftId(nextFocusLeftId);
editor.setNextFocusLeftId(nextFocusLeftId);
}
#Override
public void setNextFocusRightId(int nextFocusRightId)
{
super.setNextFocusRightId(nextFocusRightId);
face.setNextFocusRightId(nextFocusRightId);
cancel.setNextFocusRightId(nextFocusRightId);
}
#Override
public void setNextFocusUpId(int nextFocusUpId)
{
super.setNextFocusUpId(nextFocusUpId);
face.setNextFocusUpId(nextFocusUpId);
// editor.setNextFocusUpId(nextFocusUpId);
accept.setNextFocusUpId(nextFocusUpId);
cancel.setNextFocusUpId(nextFocusUpId);
}
public void setPrefillData(String prefillData)
{
this.prefillData = new String(prefillData);
}
public String setTag()
{
return tag;
}
public void setText(String text)
{
Log.d(LOGTAG, "setText(" + text + ")");
ourData = text;
updateViews();
}
private void build(Context context)
{
Log.d(LOGTAG, "build()");
addView(View.inflate(context, R.layout.textboxondemand, null));
setFocusable(true);
setFocusableInTouchMode(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setOnFocusChangeListener(this);
setOnLongClickListener(this);
face = (TextView) findViewById(R.id.TBOD_textview);
editorLayout = (RelativeLayout) findViewById(R.id.TBOD_layout);
editor = (EditText) findViewById(R.id.TBOD_edittext);
accept = (ImageButton) findViewById(R.id.TBOD_accept);
cancel = (ImageButton) findViewById(R.id.TBOD_cancel);
topGuard = (View) findViewById(R.id.TBOD_top);
btmGuard = (View) findViewById(R.id.TBOD_bottom);
face.setFocusable(true);
face.setFocusableInTouchMode(true);
face.setOnLongClickListener(this);
face.setOnHoverListener(this);
face.setOnFocusChangeListener(this);
face.setOnClickListener(this);
editor.setOnFocusChangeListener(this);
editor.setOnEditorActionListener(this);
editor.setHint(hint);
editor.setFocusable(true);
editor.setFocusableInTouchMode(true);
accept.setOnClickListener(this);
accept.setOnFocusChangeListener(this);
accept.setFocusable(true);
cancel.setFocusable(true);
cancel.setOnFocusChangeListener(this);
cancel.setOnClickListener(this);
topGuard.setFocusable(true);
topGuard.setOnFocusChangeListener(this);
btmGuard.setFocusable(true);
btmGuard.setOnFocusChangeListener(this);
editor.setNextFocusRightId(R.id.TBOD_accept);
editor.setNextFocusDownId(R.id.TBOD_bottom);
editor.setNextFocusUpId(R.id.TBOD_top);
accept.setNextFocusLeftId(R.id.TBOD_edittext);
accept.setNextFocusRightId(R.id.TBOD_cancel);
cancel.setNextFocusLeftId(R.id.TBOD_accept);
}
private void init(Context context, AttributeSet attrs)
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextBoxOnDemand);
//Use a
Log.d(LOGTAG, "init()");
if (a == null) Log.d(LOGTAG, "Did you include 'xmlns:app=\"http://schemas.android.com/apk/res-auto\"' in your root layout?");
final int N = a.getIndexCount();
for (int i = 0; i < N; ++i)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.TextBoxOnDemand_android_hint:
hint = new String(a.getString(attr));
editor.setHint(a.getString(attr));
break;
case R.styleable.TextBoxOnDemand_android_text:
ourData = new String(a.getString(attr));
break;
case R.styleable.TextBoxOnDemand_android_inputType:
int inputType = a.getInt(attr, -1);
if (inputType != -1) editor.setInputType(inputType);
break;
case R.styleable.TextBoxOnDemand_android_textColor:
textColor = a.getColorStateList(attr);
face.setTextColor(textColor);
break;
case R.styleable.TextBoxOnDemand_android_linksClickable:
face.setLinksClickable(a.getBoolean(attr, true));
break;
case R.styleable.TextBoxOnDemand_android_textColorHint:
hintColor = a.getColorStateList(attr);
break;
case R.styleable.TextBoxOnDemand_android_autoLink:
autoLinkMask = a.getInt(attr, 0);
face.setAutoLinkMask(autoLinkMask);
break;
default:
Log.d(LOGTAG, "Skipping attribute " + attr);
}
}
//Don't forget this
a.recycle();
}
private void updateViews()
{
Log.d(LOGTAG, "updateViews()");
// if (getDisplayedChild() == 0) //first child - textview
if (!inEditMode) //first child - textview
{
if (ourData.isEmpty())
{
if (hintColor != null) face.setTextColor(hintColor);
face.setText(hint);
} else
{
face.setTextColor(textColor);
face.setText(ourData);
}
face.setFocusable(true);
face.setFocusableInTouchMode(true);
face.setAutoLinkMask(autoLinkMask);
} else
{ //second child - edittext
editor.setFocusable(true);
editor.setFocusableInTouchMode(true);
if (ourData.startsWith(prefillData) || ourData.length() >= prefillData.length())
editor.setText("");
else
editor.setText(prefillData);
editor.append(ourData);
inputReady = false;
editor.requestFocus();
}
}
public void setAutoLinkMask(LinkifyEnum linkifyEnumConstant)
{
switch (linkifyEnumConstant)
{
case ALL:
autoLinkMask = Linkify.ALL;
break;
case EMAIL_ADDRESSES:
autoLinkMask = Linkify.EMAIL_ADDRESSES;
break;
case MAP_ADDRESSES:
autoLinkMask = Linkify.MAP_ADDRESSES;
break;
case PHONE_NUMBERS:
autoLinkMask = Linkify.PHONE_NUMBERS;
break;
case WEB_URLS:
autoLinkMask = Linkify.WEB_URLS;
break;
case NONE:
default:
autoLinkMask = 0;
break;
}
//set it now
face.setAutoLinkMask(autoLinkMask);
}
public enum LinkifyEnum
{
ALL, EMAIL_ADDRESSES, MAP_ADDRESSES, PHONE_NUMBERS, WEB_URLS, NONE
};
}
I'm still working out some focus-related issues but this works as intended. When I use onFocuslistener 1, you can't focus from one TextBox to the other; when the textbox itself is focusable, I can focus from one to the other just fine, but I cannot inter-focus thru children and thus can't focus on the edittext to type.
the XML file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/TBOD_textview"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:autoLink="email"
android:focusable="true"
android:focusableInTouchMode="true"
android:linksClickable="true"
android:textAppearance="?android:attr/textAppearanceMedium" />
<RelativeLayout
android:id="#+id/TBOD_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<EditText
android:id="#+id/TBOD_edittext"
android:layout_width="300dp"
android:layout_height="30dp"
android:layout_below="#+id/TBOD_textview"
android:focusable="true"
android:focusableInTouchMode="true"
android:imeOptions="actionDone"
android:inputType="none"
android:maxLines="1"
android:padding="2dp"
android:singleLine="true"
android:textColor="#android:color/black"
android:textSize="14dp" />
<ImageButton
android:id="#+id/TBOD_accept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/TBOD_edittext"
android:layout_marginLeft="15dp"
android:layout_toRightOf="#+id/TBOD_edittext"
android:background="#drawable/button_accept_selector" />
<ImageButton
android:id="#+id/TBOD_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/TBOD_edittext"
android:layout_marginLeft="5dp"
android:layout_toRightOf="#+id/TBOD_accept"
android:background="#drawable/button_cancel_selector" />
<View
android:id="#+id/TBOD_top"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_alignParentTop="true"
android:background="#android:color/transparent" />
<View
android:id="#+id/TBOD_bottom"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_alignParentBottom="true"
android:background="#android:color/transparent" />
</RelativeLayout>
</RelativeLayout>
and finally, the attrs.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TextBoxOnDemand">
<attr name="android:text" />
<attr name="android:inputType" />
<attr name="android:hint" />
<attr name="android:textColor" />
<attr name="android:textColorHint" />
<attr name="android:linksClickable" />
<attr name="android:autoLink" />
</declare-styleable>
</resources>
This is how I used it in my main xml (after including the required namespace add):
<com.shark.widget.TextBoxOnDemand
android:id="#+id/profile_email2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/profile_skypename"
android:layout_below="#+id/profile_email_placeholder"
android:hint="#string/add_email"
android:inputType="textEmailAddress"
android:textColor="#android:color/white"
android:textColorHint="#color/skype_blue" />
EDIT: I've debugged the focus issues. It turns out that giving focus to children is difficult unless you call
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
Which kinda remedies the issue but still doesn't solve it. After some while of playing around with the onFocusChange() listener still trying to get the perfect behaviour, I threw in the towel and put in added two focus guards. I realized I cannot track the loss of focus only on my container (due to it never receiving focus) but I might as well track the idea of wanting to move away from the edit field... So i went the dirty route and added two invisible bar-like views to sandwitch the edittext in between. Once they got the focus, I could hide the component and ensure they transition properly.
And there it is, now it works as it should. Thanks to all who participated.
EDIT3: final polished version, i dumped the custom tags because they simply don't work reliably enough. Lesson to be learned: if there is an android tag for something, don't bother cloning it.