Custom Android Keyboard Focus Issue - android

I'm currently working on developing a custom keyboard app that will be optimized for a device using a DPad as its primary input device.
My problem is that When the cursor is in the EditText field and you press down (e.g. KEYCODE_DPAD_DOWN), the keyboard view is not receiving focus and KeyEvents. Either nothing happens, or the element beneath the EditText in question receives focus.
Below is the relevant code.
Any help would be much appreciated. I've tried dissecting the SoftKeyboard example aswell as KeyboardView.java for hints without success.
Thanks,
Bryan
MyKeyboard.java
public class MyKeyboard extends InputMethodService {
private static final String TAG = "MyKeyboard";
private MyKeyboardView mInputView = null;
#Override public void onCreate() {
super.onCreate();
}
#Override public View onCreateInputView() {
mInputView = (MyKeyboardView) getLayoutInflater().inflate(R.layout.input, null);
// attempts to make this focusable
mInputView.setClickable(true);
mInputView.setFocusableInTouchMode(true);
mInputView.setFocusable(true);
mInputView.setEnabled(true);
return mInputView;
}
#Override public View onCreateCandidatesView() {
super.onCreateCandidatesView();
return null;
}
#Override public void onStartInputView(EditorInfo info, boolean restarting) {
super.onStartCandidatesView(info, restarting);
}
#Override public void onFinishInput() {
super.onFinishInput();
}
#Override public void onDestroy() {
super.onDestroy();
}
}
MyKeyboardView.java
public class MyKeyboardView extends TableLayout implements View.OnClickListener, View.OnFocusChangeListener {
private static final String TAG = "MyKeyboardView";
private ArrayList<Character> charList = new ArrayList<Character>();
public MyKeyboardView(Context context) {
super(context);
populateKeyboard();
this.setOnFocusChangeListener(this);
this.setOnClickListener(this);
}
public MyKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
populateKeyboard();
this.setOnFocusChangeListener(this);
this.setOnClickListener(this);
}
#Override
public void onClick(View arg0) {
Log.d(TAG, "onClick");
}
private void populateKeyboard() {
charList.add(new Character(','));
charList.add(new Character('.'));
charList.add(new Character('?'));
charList.add(new Character('<'));
charList.add(new Character('>'));
charList.add(new Character((char) 0x2798)); // arrow
charList.add(new Character((char) 0x2798)); // arrow
charList.add(new Character((char) 0x2798)); // arrow
charList.add(new Character((char) 0x005F)); // underscore
for(char c = '#'; c < 'Z'; c++) {
charList.add(new Character(c));
Log.d(TAG, "char: " + c);
}
TableRow tr = null;
for(int i=0; i<charList.size(); i++) {
if(i % 7 == 0) {
if(tr != null)
this.addView(tr);
tr = new TableRow(this.getContext());
tr.setGravity(Gravity.CENTER_HORIZONTAL);
}
TextView tv = new TextView(this.getContext());
tv.setPadding(21, 2, 21, 2);
tv.setText(charList.get(i).toString());
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 22);
tv.setTextColor(Color.WHITE);
tv.setGravity(Gravity.CENTER);
tv.setFocusable(true);
tv.setEnabled(true);
tr.addView(tv);
}
if(tr.getChildCount() > 0)
this.addView(tr);
}
#Override
public void onFocusChange(View v, boolean hasFocus) {
Log.d(TAG, "mInputView onFocusChange " + (hasFocus ? "true" : "false"));
}
}
input.xml
<?xml version="1.0" encoding="utf-8"?>
<com.weirdtuesday.mykeyboard.MyKeyboardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#FF000000"
android:focusable="true" />

Key events must be processed manually in the onKey method. To move the cursor, I use this:
if (primaryCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
int position = connection.getTextBeforeCursor(Integer.MAX_VALUE, 0)
.length();
final CharSequence selected = connection.getSelectedText(0);
if (selected != null)
connection.commitText(
mComposing.substring(0,
mComposing.length() - selected.length()), 1);
else
connection.commitText(mComposing, 1);
connection.setSelection(position + 1, position + 1);
}

Related

multiline edittext where parts are not editable, like fill in the blanks

I need to have a view which contains textview and edittext.
Example:
Yay! you made it to ______ We should hang out! feel ____ to follow me.
Above "_____" could be of any length and it should feel like a paragraph in the end. Rest of the text given above is not changeable. Just like fill in the blanks.
From my perspective, a fill-in-the-blank widget should do the following:
Allow only certain identified portions of the text to be changed. The rest of the text is locked.
Not allow cursor movement into the locked text.
Flow from line to line like EditText.
Be generalized with variable placement of blanks.
Here is an implementation of such a widget based upon EditText. Editable spans are set up using a span (BlanksSpan) extended from StyleSpan. A blank span is identified by five underscores ("_____") in the text. Cursor movement is controlled in OnSelectionChanged() and various EditText callbacks. Changes to the text is monitor by a TextWatcher and adjustments to the displayed text are made there.
Here is the video of the widget in use:
FillInBlanksEditText.java
public class FillInBlanksEditText extends android.support.v7.widget.AppCompatEditText
implements View.OnFocusChangeListener, TextWatcher {
private int mLastSelStart;
private int mLastSelEnd;
private BlanksSpan mSpans[];
private Editable mUndoChange;
private BlanksSpan mWatcherSpan;
public FillInBlanksEditText(Context context) {
super(context);
init();
}
public FillInBlanksEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FillInBlanksEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mSpans = setSpans();
setOnFocusChangeListener(this);
}
#Override
public void onRestoreInstanceState(Parcelable state) {
mSpans = null;
super.onRestoreInstanceState(state);
Editable e = getEditableText();
mSpans = e.getSpans(0, e.length(), BlanksSpan.class);
}
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
addTextChangedListener(this);
if (findInSpan(getSelectionStart(), getSelectionEnd()) != null) {
mLastSelStart = getSelectionStart();
mLastSelEnd = getSelectionEnd();
} else if (findInSpan(mLastSelStart, mLastSelEnd) == null) {
setSelection(getEditableText().getSpanStart(mSpans[0]));
}
} else {
removeTextChangedListener(this);
}
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
if (!isFocused() || mSpans == null ||
(getSelectionStart() == mLastSelStart && getSelectionEnd() == mLastSelEnd)) {
return;
}
// The selection must be completely within a Blankspan.
final BlanksSpan span = findInSpan(selStart, selEnd);
if (span == null) {
// Current selection is not within a Blankspan. Restore selection to prior location.
moveCursor(mLastSelStart);
} else if (selStart > getEditableText().getSpanStart(span) + span.getDataLength()) {
// Acceptable location for selection (within a Blankspan).
// Make sure that the cursor is at the end of the entered data. mLastSelStart = getEditableText().getSpanStart(span) + span.getDataLength();
mLastSelEnd = mLastSelStart;
moveCursor(mLastSelStart);
} else {
// Just capture the placement.
mLastSelStart = selStart;
mLastSelEnd = selEnd;
}
super.onSelectionChanged(mLastSelStart, mLastSelEnd);
}
// Safely move the cursor without directly invoking setSelection from onSelectionChange.
private void moveCursor(final int selStart) {
post(new Runnable() {
#Override
public void run() {
setSelection(selStart);
}
});
// Stop cursor form jumping on move.
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
return false;
}
});
}
#Nullable
private BlanksSpan findInSpan(int selStart, int selEnd) {
for (BlanksSpan span : mSpans) {
if (selStart >= getEditableText().getSpanStart(span) &&
selEnd <= getEditableText().getSpanEnd(span)) {
return span;
}
}
return null;
}
// Set up a Blankspan to cover each occurrence of BLANKS_TOKEN.
private BlanksSpan[] setSpans() {
Editable e = getEditableText();
String s = e.toString();
int offset = 0;
int blanksOffset;
while ((blanksOffset = s.substring(offset).indexOf(BLANKS_TOKEN)) != -1) {
offset += blanksOffset;
e.setSpan(new BlanksSpan(Typeface.BOLD), offset, offset + BLANKS_TOKEN.length(),
Spanned.SPAN_INCLUSIVE_INCLUSIVE);
offset += BLANKS_TOKEN.length();
}
return e.getSpans(0, e.length(), BlanksSpan.class);
}
// Check change to make sure that it is acceptable to us.
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mWatcherSpan = findInSpan(start, start + count);
if (mWatcherSpan == null) {
// Change outside of a Blankspan. Just put things back the way they were.
// Do this in afterTextChaanged. mUndoChange = Editable.Factory.getInstance().newEditable(s);
} else {
// Change is OK. Track data length.
mWatcherSpan.adjustDataLength(count, after);
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing...
}
#Override
public void afterTextChanged(Editable s) {
if (mUndoChange == null) {
// The change is legal. Modify the contents of the span to the format we want.
CharSequence newContents = mWatcherSpan.getFormattedContent(s);
if (newContents != null) {
removeTextChangedListener(this);
int selection = getSelectionStart();
s.replace(s.getSpanStart(mWatcherSpan), s.getSpanEnd(mWatcherSpan), newContents);
setSelection(selection);
addTextChangedListener(this);
}
} else {
// Illegal change - put things back the way they were.
removeTextChangedListener(this);
setText(mUndoChange);
mUndoChange = null;
addTextChangedListener(this);
}
}
#SuppressWarnings("WeakerAccess")
public static class BlanksSpan extends StyleSpan {
private int mDataLength;
public BlanksSpan(int style) {
super(style);
}
#SuppressWarnings("unused")
public BlanksSpan(#NonNull Parcel src) {
super(src);
}
public void adjustDataLength(int count, int after) {
mDataLength += after - count;
}
#Nullable
public CharSequence getFormattedContent(Editable e) {
if (mDataLength == 0) {
return BLANKS_TOKEN;
}
int spanStart = e.getSpanStart(this);
return (e.getSpanEnd(this) - spanStart > mDataLength)
? e.subSequence(spanStart, spanStart + mDataLength)
: null;
}
public int getDataLength() {
return mDataLength;
}
}
#SuppressWarnings({"FieldCanBeLocal", "unused"})
private static final String TAG = "FillInBlanksEditText";
private static final String BLANKS_TOKEN = "_____";
}
activity_main.java
A sample layout.
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.fillintheblanks.FillInBlanksEditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#android:color/transparent"
android:inputType="textMultiLine"
android:padding="16dp"
android:text="Yay! You made it to _____. We should hang out! Feel _____ to follow me."
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.fillintheblanks.FillInBlanksEditText
android:id="#+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#android:color/transparent"
android:inputType="textMultiLine"
android:padding="16dp"
android:text="_____ says that it is time to _____. Are you _____?"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="#id/editText" />
</android.support.constraint.ConstraintLayout>
A few things to note:
In extracted mode, cursor placement jumps around if a touch is made outside of a BlanksSpan. Things still work but misbehave a little.
The length of the blanks fields is fixed, but it can be made variable in length with some additional work.
The action mode in the control needs some work based upon requirements.
multiline edittext where parts are not editable, like fill in the blanks
You can use a TextWatcher() for this requirement
Try this he is the little work around for this
MainActivity
public class MainActivity extends AppCompatActivity {
EditText myEditText;
String startText = "I'm The First Part";
String lastText = "I'm The Last Part";
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SpannableStringBuilder firstStringBuilder = new SpannableStringBuilder(startText);
final SpannableStringBuilder lastStringBuilder = new SpannableStringBuilder(lastText);
StyleSpan firstStyleSpan = new StyleSpan(android.graphics.Typeface.BOLD);
firstStringBuilder.setSpan(firstStyleSpan, 0, firstStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
lastStringBuilder.setSpan(firstStyleSpan, 0, lastStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
myEditText = findViewById(R.id.myEditText);
spannableStringBuilder.append(firstStringBuilder);
spannableStringBuilder.append(" ");
spannableStringBuilder.append(lastStringBuilder);
myEditText.setText(spannableStringBuilder);
Selection.setSelection(myEditText.getText(), startText.length() + 1);
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
if (!s.toString().startsWith(firstStringBuilder.toString())
|| !s.toString().contains(lastText)) {
Log.e("StringBuilder_TAG", spannableStringBuilder.toString());
myEditText.setText(spannableStringBuilder);
Selection.setSelection(myEditText.getText(), myEditText.getText().length() - lastStringBuilder.length() - 1);
} else {
spannableStringBuilder.clear();
spannableStringBuilder.append(s.toString());
Log.e("My_TAG", spannableStringBuilder.toString());
}
}
});
}
}
layout.activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="#+id/myEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:padding="5dp" />
</LinearLayout>
Here is the output video of above code https://www.youtube.com/watch?v=pfhUzLiFD6U
using above code you able to make not editble first and last parts of editext
Note
You can also use a TextDrawable
here are some links for that
How to put text in a drawable?
https://github.com/amulyakhare/TextDrawable
Set unchangeable some part of editText android
You can also create a custom EditText for this
Adding a prefix to an EditText
You can also use InputFilter
Solution one
Try using flexbox-layout - https://github.com/google/flexbox-layout.
Solution Two
Use textWatcher
Solution Three
Use html, css and javascript to design a simple webpage.
Use webview to load the html file.
Follow #Cheticamp 's answer, it works when you set a SpannableString in a setText() method.
Also you should override setText() method and set Spans to mSpans:
#Override
public void setText(CharSequence text, BufferType type) {
mSpans = Editable.Factory.getInstance().newEditable(text).getSpans(0, text.length(), BlanksSpan.class);
Log.d(TAG, "setText: " + mSpans.length);
super.setText(text, type);
}
if you don't set a SpannableString, Editable interface will get a simple String without Spans.
So if you call methods like:
Editable.getSpanStart(tag) returns -1;
Editable.getSpanEnd(tag) returns -1;
Editable.getSpans(start, end, Class) returns empty array

Why PopWindow make the screen black for a short time when the Activity finish

private void openInput(View v, String nickname) {
if (popInputNickName == null) {
View view = LayoutInflater.from(UserInfoActivity.this).inflate(R.layout.pop_nickname_modify, null);
popInputNickName = new InputPopWindow(view, LinearLayout.LayoutParams.MATCH_PARENT
, LinearLayout.LayoutParams.WRAP_CONTENT, true);
popInputNickName.setAnimationStyle(R.style.NewContentAnim);
popInputNickName.setBackgroundDrawable(new BitmapDrawable());
popInputNickName.setFocusable(true);
popInputNickName.setTouchable(true);
popInputNickName.setOutsideTouchable(true);
edit_nickname = (EditText) view.findViewById(R.id.edit_pop_nickname);
text_commit = (TextView) view.findViewById(R.id.text_pop_commit);
text_cancel = (TextView) view.findViewById(R.id.text_pop_cancel);
if (!TextUtils.isEmpty(nickname)) {
edit_nickname.setText(nickname);
}
text_commit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String nickName = edit_nickname.getText().toString().trim();
if (!TextUtils.isEmpty(nickName)) {
modifyNickName(nickName);
} else {
ToastUtil.Infotoast(UserInfoActivity.this, getString(R.string.null_nickname));
}
}
});
text_cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popInputNickName.dismiss();
}
});
} else {
if (!TextUtils.isEmpty(nickname)) {
edit_nickname.setText(nickname);
}
}
popInputNickName.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
popInputNickName.showAtLocation(v, Gravity.BOTTOM, 0, 0);
edit_nickname.requestFocus();
InputMethodManager imm = (InputMethodManager) edit_nickname.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
openInputTime = System.currentTimeMillis();
}
the popwindow show
When I enter to an activity(call it A) & shows a popwindow to modify user's nickname. When I finish A, the screen will be black for a while. But when i enter A and not open the popwindow, then no black screen appears, it completes as normal. So i wonder what caused this problem and how to fix it.
I am so sorry that the question is not clear. In the end I solved the problem.I used a custom-view that caused this problem.The edit_nickname in the PopWindow is a custom-view that extends EditText.
public class TfEditView extends EditText {
private OnFinishComposingListener mFinishComposingListener;
public TfEditView(Context context) {
super(context);
}
public TfEditView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TfEditView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnFinishComposingListener(OnFinishComposingListener listener) {
this.mFinishComposingListener = listener;
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new MyInputConnection(super.onCreateInputConnection(outAttrs), false);
}
public class MyInputConnection extends InputConnectionWrapper {
public MyInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
#Override
public boolean finishComposingText() {
boolean finishComposing = super.finishComposingText();
if (mFinishComposingListener != null) {
mFinishComposingListener.finishComposing();
}
return finishComposing;
}
}
public interface OnFinishComposingListener {
void finishComposing();
}
}
private long openInputTime = 0;
private final long canHideTime = 500;
private void openInput(View v, String nickname) {
if (popInputNickName == null) {
View view = LayoutInflater.from(UserInfoActivity.this).inflate(R.layout.pop_nickname_modify, null);
popInputNickName = new InputPopWindow(view, LinearLayout.LayoutParams.MATCH_PARENT
, LinearLayout.LayoutParams.WRAP_CONTENT, true);
popInputNickName.setAnimationStyle(R.style.NewContentAnim);
popInputNickName.setBackgroundDrawable(new BitmapDrawable());
popInputNickName.setFocusable(true);
popInputNickName.setTouchable(true);
popInputNickName.setOutsideTouchable(true);
edit_nickname = (TfEditView) view.findViewById(R.id.edit_pop_nickname);
text_commit = (TextView) view.findViewById(R.id.text_pop_commit);
text_cancel = (TextView) view.findViewById(R.id.text_pop_cancel);
if (!TextUtils.isEmpty(nickname)) {
edit_nickname.setText(nickname);
}
edit_nickname.setOnFinishComposingListener(new TfEditView.OnFinishComposingListener() {
#Override
public void finishComposing() {
if (popInputNickName != null && popInputNickName.isShowing() && openInputTime != 0 && (System.currentTimeMillis() - openInputTime > canHideTime)) {
popInputNickName.dismiss();
}
}
});
text_commit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String nickName = edit_nickname.getText().toString().trim();
if (!TextUtils.isEmpty(nickName)) {
modifyNickName(nickName);
} else {
ToastUtil.Infotoast(UserInfoActivity.this, getString(R.string.null_nickname));
}
}
});
text_cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popInputNickName.dismiss();
}
});
} else {
if (!TextUtils.isEmpty(nickname)) {
edit_nickname.setText(nickname);
}
}
popInputNickName.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
popInputNickName.showAtLocation(v, Gravity.BOTTOM, 0, 0);
edit_nickname.requestFocus();
InputMethodManager imm = (InputMethodManager) edit_nickname.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
openInputTime = System.currentTimeMillis();
}
In the code above ,the popindow can dismissed longer than 500ms from the popwindow begin to show.

Handling tab press inside a activity group to do back press functionality

I am Implementing tabhost with 5 tabs.On 5th tab i have an activitygroup with 2 child activity.From child activity if i press back button the app returns to the parent activity.
But what i need is on pressing tab button too it has to return to the parent activity.
this is my activity group:
public class Activitygroup extends ActivityGroup {
private Stack<String> stack;
public static Activitygroup grp;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
grp = new Activitygroup();
if (stack == null) {
stack = new Stack<String>();
}
push("HomeStackActivity", new Intent(this,Extras.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
}
#Override
public void finishFromChild(Activity child) {
pop();
}
#Override
public void onBackPressed() {
pop();
}
public void push(String id, Intent intent) {
Window window = getLocalActivityManager().startActivity(id,
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
if (window != null) {
stack.push(id);
setContentView(window.getDecorView());
}
}
public void pop() {
if (stack.size() == 1) {
finish();
}
LocalActivityManager manager = getLocalActivityManager();
manager.destroyActivity(stack.pop(), true);
if (stack.size() > 0) {
Intent lastIntent = manager.getActivity(stack.peek()).getIntent()
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Window newWindow = manager.startActivity(stack.peek(), lastIntent);
setContentView(newWindow.getDecorView());
}
}
this is where i am handling second tab press in tabhost activity:
int numberOfTabs = tabHost.getTabWidget().getChildCount();
for (int t = 0; t < numberOfTabs; t++) {
tabHost.getTabWidget().getChildAt(t).setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
String currentSelectedTag = MainActivity.this.getTabHost().getCurrentTabTag();
String currentTag = (String) v.getTag();
if (currentSelectedTag.equalsIgnoreCase(currentTag)) {
MainActivity.this.getTabHost().setCurrentTabByTag(currentTag);
String newSelectedTabTag = MainActivity.this.getTabHost().getCurrentTabTag();
if (newSelectedTabTag.toLowerCase().indexOf("extras") != -1) {
"BACKPRESS FUNCTIONALITY"-MUST BRING THE PARENT ACTIVITY ON TOP HERE
}
return true;
}
}
return false;
}
});
}
Iphone has this functionality by default.On pressing tab on current activity it bring the parent activity on top.Please suggest me some workaround for this.thanks in advance!!!
This code is used to sense the tab press from the child activity
int numberOfTabs = tabHost.getTabWidget().getChildCount();
for (int t = 0; t < numberOfTabs; t++) {
tabHost.getTabWidget().getChildAt(t).setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
String currentSelectedTag = MainActivity.this.getTabHost().getCurrentTabTag();
String currentTag = (String) v.getTag();
if (currentSelectedTag.equalsIgnoreCase(currentTag)) {
MainActivity.this.getTabHost().setCurrentTabByTag(currentTag);
String newSelectedTabTag = MainActivity.this.getTabHost().getCurrentTabTag();
if (newSelectedTabTag.toLowerCase().indexOf("extras") != -1) {
****Here call a static method to check whether the child activity is active****
childactivity.getappcontext();
}
return true;
}
}
return false;
}
});
}
In child activity paste this code.If the child activity is active then it'll be closed when pressing the tab hence the parent activity will be visible.
#Override
public void onStart() {
super.onStart();
active=true;
}
#Override
public void onStop() {
super.onStop();
active=false;
}
public static void getAppContext() {
if(active){
System.out.println("gallery");
galleryActivity1.finish();
}
}
Feel free to ask if not clear.Would love to help.

Android: Adding functionality that activty changes when game is complete

I wish to add the following functionality to my game:
-When the game is complete (no more cards are visible on screen) then move to a new activity
I am aware how to move to another activty using intents but I am not sure how to implement the functionality in this case.
I.e. what variable/info can I use to ensure the game is complete when I move before moving to the next activity?
For reference, The game is based off this open source game Images of the game are shown here to give an idea.
Current code:
public class Manager extends Activity {
private static int ROW_COUNT = -1;
private static int COL_COUNT = -1;
private Context context;
private Drawable backImage;
private int [] [] cards;
private List<Drawable> images;
private Card firstCard;
private Card seconedCard;
private ButtonListener buttonListener;
private static Object lock = new Object();
int turns;
private TableLayout mainTable;
private UpdateCardsHandler handler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new UpdateCardsHandler();
loadImages();
setContentView(R.layout.main);
TextView url = ((TextView)findViewById(R.id.myWebSite));
Linkify.addLinks(url, Linkify.WEB_URLS);
backImage = getResources().getDrawable(R.drawable.icon);
/*
((Button)findViewById(R.id.ButtonNew)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
newGame();
}
});*/
buttonListener = new ButtonListener();
mainTable = (TableLayout)findViewById(R.id.TableLayout03);
context = mainTable.getContext();
Spinner s = (Spinner) findViewById(R.id.Spinner01);
ArrayAdapter adapter = ArrayAdapter.createFromResource(
this, R.array.type, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
s.setAdapter(adapter);
s.setOnItemSelectedListener(new OnItemSelectedListener(){
#Override
public void onItemSelected(
android.widget.AdapterView<?> arg0,
View arg1, int pos, long arg3){
((Spinner) findViewById(R.id.Spinner01)).setSelection(0);
int x,y;
switch (pos) {
case 1:
x=4;y=4;
break;
case 2:
x=4;y=5;
break;
case 3:
x=4;y=6;
break;
case 4:
x=5;y=6;
break;
case 5:
x=6;y=6;
break;
default:
return;
}
newGame(x,y);
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
}
private void newGame(int c, int r) {
ROW_COUNT = r;
COL_COUNT = c;
cards = new int [COL_COUNT] [ROW_COUNT];
mainTable.removeView(findViewById(R.id.TableRow01));
mainTable.removeView(findViewById(R.id.TableRow02));
TableRow tr = ((TableRow)findViewById(R.id.TableRow03));
tr.removeAllViews();
mainTable = new TableLayout(context);
tr.addView(mainTable);
for (int y = 0; y < ROW_COUNT; y++) {
mainTable.addView(createRow(y));
}
firstCard=null;
loadCards();
turns=0;
((TextView)findViewById(R.id.tv1)).setText("Tries: "+turns);
}
private void loadImages() {
images = new ArrayList<Drawable>();
images.add(getResources().getDrawable(R.drawable.card1));
images.add(getResources().getDrawable(R.drawable.card2));
images.add(getResources().getDrawable(R.drawable.card3));
images.add(getResources().getDrawable(R.drawable.card4));
images.add(getResources().getDrawable(R.drawable.card5));
images.add(getResources().getDrawable(R.drawable.card6));
images.add(getResources().getDrawable(R.drawable.card7));
images.add(getResources().getDrawable(R.drawable.card8));
images.add(getResources().getDrawable(R.drawable.card9));
images.add(getResources().getDrawable(R.drawable.card10));
images.add(getResources().getDrawable(R.drawable.card11));
images.add(getResources().getDrawable(R.drawable.card12));
images.add(getResources().getDrawable(R.drawable.card13));
images.add(getResources().getDrawable(R.drawable.card14));
images.add(getResources().getDrawable(R.drawable.card15));
images.add(getResources().getDrawable(R.drawable.card16));
images.add(getResources().getDrawable(R.drawable.card17));
images.add(getResources().getDrawable(R.drawable.card18));
images.add(getResources().getDrawable(R.drawable.card19));
images.add(getResources().getDrawable(R.drawable.card20));
images.add(getResources().getDrawable(R.drawable.card21));
}
private void loadCards(){
try{
int size = ROW_COUNT*COL_COUNT;
Log.i("loadCards()","size=" + size);
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i=0;i<size;i++){
list.add(new Integer(i));
}
Random r = new Random();
for(int i=size-1;i>=0;i--){
int t=0;
if(i>0){
t = r.nextInt(i);
}
t=list.remove(t).intValue();
cards[i%COL_COUNT][i/COL_COUNT]=t%(size/2);
Log.i("loadCards()", "card["+(i%COL_COUNT)+
"]["+(i/COL_COUNT)+"]=" + cards[i%COL_COUNT][i/COL_COUNT]);
}
}
catch (Exception e) {
Log.e("loadCards()", e+"");
}
}
private TableRow createRow(int y){
TableRow row = new TableRow(context);
row.setHorizontalGravity(Gravity.CENTER);
for (int x = 0; x < COL_COUNT; x++) {
row.addView(createImageButton(x,y));
}
return row;
}
private View createImageButton(int x, int y){
Button button = new Button(context);
button.setBackgroundDrawable(backImage);
button.setId(100*x+y);
button.setOnClickListener(buttonListener);
return button;
}
class ButtonListener implements OnClickListener {
#Override
public void onClick(View v) {
synchronized (lock) {
if(firstCard!=null && seconedCard != null){
return;
}
int id = v.getId();
int x = id/100;
int y = id%100;
turnCard((Button)v,x,y);
}
}
private void turnCard(Button button,int x, int y) {
button.setBackgroundDrawable(images.get(cards[x][y]));
if(firstCard==null){
firstCard = new Card(button,x,y);
}
else{
if(firstCard.x == x && firstCard.y == y){
return; //the user pressed the same card
}
seconedCard = new Card(button,x,y);
turns++;
((TextView)findViewById(R.id.tv1)).setText("Tries: "+turns);
TimerTask tt = new TimerTask() {
#Override
public void run() {
try{
synchronized (lock) {
handler.sendEmptyMessage(0);
}
}
catch (Exception e) {
Log.e("E1", e.getMessage());
}
}
};
Timer t = new Timer(false);
t.schedule(tt, 1300);
}
}
}
class UpdateCardsHandler extends Handler{
#Override
public void handleMessage(Message msg) {
synchronized (lock) {
checkCards();
}
}
public void checkCards(){
if(cards[seconedCard.x][seconedCard.y] == cards[firstCard.x][firstCard.y]){
firstCard.button.setVisibility(View.INVISIBLE);
seconedCard.button.setVisibility(View.INVISIBLE);
}
else {
seconedCard.button.setBackgroundDrawable(backImage);
firstCard.button.setBackgroundDrawable(backImage);
}
firstCard=null;
seconedCard=null;
}
}
}
The easiest way to do this would be to check win conditions with an if statement. This should be done in the method when a turn is actually taken which I assume happens in the turnCard() method.
if (winConditionMet) {
displayWinningScreen();
} else if (lossConditionMet) {
displayLosingScreen();
}
If conditions have been met, then call a method which handles wrapping up that screen, and then launching a new activity. For instance you could add a button to the screen with whatever text you wanted, that when pushed, would take the user to the next screen, be it your score screen, replay screen, main menu, or what have you.
Edit: Okay, since this is a game of memory, you could iterate through the cards at the end of every turn taken and check if any card still has its image set to backImage. If there are none left that are set to backImage, you can then end the game with your code inside of the if statement.
Or, instead of using an ArrayList, you could use some form of Map to keep track of if each card has been permanently turned up or not with the boolean value.

ClassCastExcptionAndroid

MainActivity
public class MainActivity extends Activity {
// Declare our Views, so we can access them later
private CheckUsernameEditText etUsername;
private EditText etPassword;
private EditText etPassword2;
private Button btnRegister;
private Button btnCancel;
private TextView lblUserStatus;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set Activity Layout
setContentView(R.layout.activity_main);
// Get the EditText and Button References
etUsername = (CheckUsernameEditText) findViewById(R.id.username);
etPassword = (EditText) findViewById(R.id.password);
etPassword2 = (EditText) findViewById(R.id.password2);
btnRegister = (Button) findViewById(R.id.register_button);
btnCancel = (Button) findViewById(R.id.cancel_button);
lblUserStatus = (TextView) findViewById(R.id.userstatus);
// Set our new Listener to the Username EditText view
etUsername.setOnUsernameAvailableListener(new OnUsernameAvailableListener() {
#Override
public void onAvailableChecked(String username,
boolean available) {
// Handle the event here
if (!available) {
etUsername.setTextColor(Color.RED);
lblUserStatus
.setText(username
+ " is already taken. Please choose another login name.");
} else {
etUsername.setTextColor(Color.GREEN);
lblUserStatus.setText(username + " is available.");
}
}
});
// Set Click Listener
btnRegister.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// create Account
}
});
btnCancel.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Close the application
finish();
}
});
}
The corresponding XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
*
*
<EditText
android:id="#+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
*
*
</LinearLayout>
CheckUsernameEditText
public class CheckUsernameEditText extends EditText implements OnKeyListener {
OnUsernameAvailableListener onUsernameAvailableListener = null;
final private static String[] registeredUsers = new String[] {
// This is just a fixed List for tutorial purposes
// in a real application you'd check this server sided or inside the
// database
"tseng", "admin", "root", "joedoe", "john" };
public CheckUsernameEditText(Context context) {
super(context);
// Set KeyListener to ourself
this.setOnKeyListener(this);
}
public CheckUsernameEditText(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
// Set KeyListener to ourself
this.setOnKeyListener(this);
}
public CheckUsernameEditText(Context context, AttributeSet attrs) {
super(context, attrs);
// Set KeyListener to ourself
this.setOnKeyListener(this);
}
// Allows the user to set an Listener and react to the event
public void setOnUsernameAvailableListener(
OnUsernameAvailableListener listener) {
onUsernameAvailableListener = listener;
}
// This function is called after the check was complete
private void OnUserChecked(String username, boolean available) {
// Check if the Listener was set, otherwise we'll get an Exception when
// we try to call it
if (onUsernameAvailableListener != null) {
// Only trigger the event, when we have a username
if (!TextUtils.isEmpty(username)) {
onUsernameAvailableListener.onAvailableChecked(username,
available);
}
}
}
#Override
public boolean onKey(View v, int keycode, KeyEvent keyevent) {
// We only want to handle ACTION_UP events, when user releases a key
if (keyevent.getAction() == KeyEvent.ACTION_DOWN)
return false;
boolean available = true;
// Whenever a user press a key, check if the username is available
String username = getText().toString().toLowerCase();
if (!TextUtils.isEmpty(username)) {
// Only perform check, if we have anything inside the EditText box
for (int i = 0; i < registeredUsers.length; i++) {
if (registeredUsers[i].equals(username)) {
available = false;
// Finish the loop, as the name is already taken
break;
}
}
// Trigger the Event and notify the user of our widget
OnUserChecked(username, available);
return false;
}
return false;
}
// Define our custom Listener interface
public interface OnUsernameAvailableListener {
public abstract void onAvailableChecked(String username,
boolean available);
}
}
The problem is that I take a classclastexception. Because I declare on the xml the username as edittext and in the main code i declare it as CheckUsernameEditText. How I can solve this problem?Why the casting isn't working, especially now that the CheckUsernameEditText extends the EditText class?
All CheckUsernameEditText objects are EditText objects,
but not all EditText objects are CheckUsernameEditText objects.
You should use your custom class in the XML:
<your.package.name.CheckUsernameEditText
android:id="#+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
I believe if you have a custom View (in your case CheckUsernameEditText) you have to declare it as such in the XML... Remember that as #Sam points out you can't cast downward into a derived class, you can only cast upward into a parent class, so you can always cast your CheckUsernameEditText View up to an EditText (or just View) but you can't go the other way.

Categories

Resources