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
}
Related
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" />
I'm using a custom Collapsing Toolbar Layout, which has a Title and a Subtitle.
I got the title to collapse and animate on a curved path, but the part of the title becoming smaller as collapsing isn't smooth. It resizes in a jagged sort of way.
This is my behavior that is responsible for moving and resizing the title:
public class ViewBehavior : CoordinatorLayout.Behavior
{
private Context mContext;
private int mStartMarginRight;
private int mEndMargintRight;
private int mMarginLeft;
private int mStartMarginBottom;
private bool isHide;
private static float SCALE_MINIMUM = 0.5f;
public ViewBehavior(Context context, IAttributeSet attrs)
{
mContext = context;
}
public override bool LayoutDependsOn(CoordinatorLayout parent, Java.Lang.Object child, View dependency)
{
return dependency is AppBarLayout;
}
public override bool OnDependentViewChanged(CoordinatorLayout parent, Java.Lang.Object child, View dependency)
{
ShouldInitProperties((child as HeaderView), dependency);
int maxScroll = ((AppBarLayout)dependency).TotalScrollRange;
float percentage = System.Math.Abs(dependency.GetY()) / (float)maxScroll;
float childPosition = dependency.Height
+ dependency.GetY()
- (child as View).Height
- (getToolbarHeight() - (child as View).Height) * percentage / 2;
childPosition = childPosition - mStartMarginBottom * (1f - percentage);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)(child as View).LayoutParameters;
lp.RightMargin = (int)(100 * System.Math.Sin(percentage * System.Math.PI)) + mStartMarginRight / 2 + mEndMargintRight / 2;
lp.LeftMargin = mMarginLeft;
(child as View).LayoutParameters = lp;
(child as View).SetY(childPosition);
float x = (child as HeaderView).Title.TextSize;
//Here is the algorithm for setting the text size
(child as HeaderView).Title.SetTextSize(ComplexUnitType.Sp, 36 * (1 - percentage / 2));
(child as HeaderView).SubTitle.SetTextSize(ComplexUnitType.Sp, 26 * (1 - percentage / 2));
var toolbarTitleSize = (int)TypedValue.ApplyDimension(ComplexUnitType.Sp, 18, Application.Context.Resources.DisplayMetrics);
var toolbarSubTitleSize = (int)TypedValue.ApplyDimension(ComplexUnitType.Sp, 16, Application.Context.Resources.DisplayMetrics);
if ((child as HeaderView).Title.TextSize < toolbarTitleSize)
(child as HeaderView).Title.SetTextSize(ComplexUnitType.Sp, 18);
if ((child as HeaderView).SubTitle.TextSize < toolbarSubTitleSize)
(child as HeaderView).SubTitle.SetTextSize(ComplexUnitType.Sp, 14);
if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
{
if (isHide && percentage < 1)
{
(child as View).Visibility = ViewStates.Visible;
isHide = false;
}
else if (!isHide && percentage == 1)
{
(child as View).Visibility = ViewStates.Gone;
isHide = true;
}
}
return true;
}
public void ShouldInitProperties(HeaderView child, View dependency)
{
if (mStartMarginRight == 0)
mStartMarginRight = mContext.Resources.GetDimensionPixelOffset(Resource.Dimension.header_view_start_margin_right);
if (mEndMargintRight == 0)
mEndMargintRight = mContext.Resources.GetDimensionPixelOffset(Resource.Dimension.header_view_end_margin_right);
if (mStartMarginBottom == 0)
mStartMarginBottom = mContext.Resources.GetDimensionPixelOffset(Resource.Dimension.header_view_start_margin_bottom);
if (mMarginLeft == 0)
mMarginLeft = mContext.Resources.GetDimensionPixelOffset(Resource.Dimension.header_view_end_margin_left);
}
public int getToolbarHeight()
{
int result = 0;
TypedValue tv = new TypedValue();
if (mContext.Theme.ResolveAttribute(Android.Resource.Attribute.ActionBarSize, tv, true))
{
result = TypedValue.ComplexToDimensionPixelSize(tv.Data, mContext.Resources.DisplayMetrics);
}
return result;
}
}
How can I change the algorithm so it should resize in a smoother fashion?
Edit - Video:
https://youtu.be/j6LseSW6h1s
As mentioned by others scaling via textSize doesn't work well on Android within animations since it isn't accurate enough (it rounds up the decimal values to integers).
If it fulfills your need you should perform your animation with the scaleX/scaleY attributes, e.g.:
float scale = 1 - percentage * SCALE_MINIMUM;
(child as HeaderView).Title.SetScaleX(scale);
(child as HeaderView).Title.SetScaleY(scale);
(child as HeaderView).SubTitle.SetScaleX(scale);
(child as HeaderView).SubTitle.SetScaleY(scale);
The problem you have is that the even if you calculate scales as demical values, they become integer values in the TextView. You should enable both LINEAR_TEXT_FLAG and SUBPIXEL_TEXT_FLAG flags in your TextView's Paint class to achieve smooth scaling and positioning.
Something like this:
yourTextView.Paint.SubpixelText = true;
yourTextView.Paint.LinearText = true;
I have use this layout format for collapsing toolbar and it works smoothly.
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="#dimen/height_300dp"
android:background="?colorPrimary">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsible_Toolbar_Layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:collapsedTitleTextAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
app:contentScrim="#color/colorDarkBlue"
app:expandedTitleMarginEnd="#dimen/margin_64dp"
app:expandedTitleMarginStart="#dimen/margin_20dp"
app:expandedTitleTextAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:fitsSystemWindows="true"
android:focusableInTouchMode="true"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorTransparent" />
<ImageButton
android:id="#+id/ib"
android:layout_width="#dimen/margin_35dp"
android:layout_height="#dimen/margin_35dp"
android:layout_gravity="right"
android:layout_marginRight="#dimen/margin_10dp"
android:layout_marginTop="#dimen/height_245dp"
android:background="#null"
android:src="#drawable/ic_launcher" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="#android:color/transparent"
android:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:contentInsetStart="#dimen/margin_50dp"
app:layout_collapseMode="pin">
<ImageButton
android:id="#+id/ib2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="#dimen/margin_10dp"
android:background="#null"
android:popupTheme="#style/ThemeOverlay.AppCompat.Light"
android:src="#drawable/ic_launcher"
android:visibility="invisible" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
After that in Fragment in IntializeComponents I am intialize this layout only and set Text
appBarLayout = (AppBarLayout)view.findViewById(R.id.app_bar_layout);
CollapsingToolbarLayout toolbarLayout = (CollapsingToolbarLayout) view.findViewById(R.id.collapsible_Toolbar_Layout);
toolbarLayout.setTitle(yourTitle);
toolbarLayout.setCollapsedTitleTextColor(Color.WHITE);
toolbarLayout.setExpandedTitleColor(Color.WHITE);
appBarLayout.addOnOffsetChangedListener(this);
Add this method for Handle the toolbar
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
int maxScroll = appBarLayout.getTotalScrollRange();
float percentage = (float) Math.abs(offset) / (float) maxScroll;
handleToolbarTitleVisibility(percentage);
}
private void handleToolbarTitleVisibility(float percentage) {
if (percentage >= PERCENTAGE_TO_SHOW_TITLE_AT_TOOLBAR) {
if(!mIsTheTitleVisible) {
imageButton.setVisibility(View.VISIBLE);
ib.setVisibility(View.INVISIBLE);
mIsTheTitleVisible = true;
}
} else {
if (mIsTheTitleVisible) {
imageButton.setVisibility(View.INVISIBLE);
ibMap.setVisibility(View.VISIBLE);
mIsTheTitleVisible = false;
}
}
}
I hope it will helps you :)
No if you are using customTextview then also it will work because I am also using customTextView only.
Will you post the customtextview code here? So all can see the problem in your customTextview.
I’m working on an app in which I want to drag a button. I have written two programs. The problem is that each of them is fulfilling only a specific part of what I want. I want a button which can be moved freely on the screen (no disappearing and stuff) and I also need the program to give me the coordinates of the local button position while moving (permanent).
In the first program is the problem that the button disappears while dragging. This happens for the negative values of x and y and also for positive x values (not for positive y values; here is everything perfect). Positive y values are everything above the button, positive x values everything on the right side of the button and so on. For the negative values the button disappears like there is wall which covers the button. For the positive x values the button vanishes with rising x value (a little bit like a fizzing tablet in water).
Here is the code:
seat_leftright = (ImageButton) findViewById(R.id.seat_leftright);
seat_leftright.setOnTouchListener(new OnTouchListener() {
int k = 0;
int prevX,prevY;
int x=0;
int y=0;
#Override
public boolean onTouch(final View v,final MotionEvent event)
{
final LinearLayout.LayoutParams par=(LinearLayout.LayoutParams)v.getLayoutParams();
switch(event.getAction())
{
case MotionEvent.ACTION_MOVE:
{
if(k == 0){ //otherwise there would be an offset when touching the button the first time
prevY=(int)event.getRawY();
prevX=(int)event.getRawX();
}
y+=prevY -(int)event.getRawY();
prevY=(int)event.getRawY();
par.bottomMargin = y;
x+=(int)event.getRawX()-prevX;
prevX=(int)event.getRawX();
par.leftMargin = x;
Log.i("LOG","x: "+ x +", y: " + y );
k++;
v.setLayoutParams(par);
return true;
}
case MotionEvent.ACTION_UP:
{
par.bottomMargin=0;
par.leftMargin=0;
k=0;
y=0;
x=0;
v.setLayoutParams(par);
return true;
}
}
return false;
}
});
}
In the second program I used the DragShadowBuilder function. The button works perfectly in this program, so it is not disappearing or the like. Here I have problems with receiving the values. I constantly need the x and y position of the button while moving. I tried it with the Action_drag_location, but it only returns the coordinates when I’m dragging the button above another button (here it is the button “arrow_down”). Replacing the “arrow_down” button with my background for constantly receiving the coordinates didn’t work at all. I also tried to combine my first program with the second with the result that I didn’t received any values at all.
I hope you can help me with this. I’m grateful for every kind of help!
Below the code of the second program.
OnTouchListener myOnTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(
view);
view.startDrag(null, shadowBuilder, view, 0);
view.setVisibility(View.INVISIBLE);
return true;
}
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
view.setVisibility(View.VISIBLE);}
return true;
}
};
OnDragListener myDragListener = new OnDragListener() {
#Override
public boolean onDrag(View layoutview, DragEvent dragevent) {
int action = dragevent.getAction();
View view = (View) dragevent.getLocalState();
switch (action) {
case DragEvent.ACTION_DRAG_STARTED:
Log.i("LOG","DragStarted");
break;
case DragEvent.ACTION_DRAG_ENTERED:
break;
case DragEvent.ACTION_DRAG_LOCATION:
float x = (int)layoutview.getX();
float y = (int)layoutview.getY();
Log.i("COORDS","X: " + x +" Y: " + y);
break;
case DragEvent.ACTION_DRAG_EXITED:
break;
case DragEvent.ACTION_DROP:
break;
case DragEvent.ACTION_DRAG_ENDED:
Log.d("LOG", "Drag ended");
if (dropEventNotHandled(dragevent)) {
view.setVisibility(View.VISIBLE);
}
break;
default:
break;
}
return true;
}
private boolean dropEventNotHandled(DragEvent dragEvent) {
return !dragEvent.getResult();
}
};
findViewById(R.id.arrow_up).setOnTouchListener(myOnTouchListener);
findViewById(R.id.arrow_down).setOnDragListener(myDragListener);
#Jay: Thanks for your help, but that didn't solve the problem. The disappearing buttons were due to my layout, where I had this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/seat" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="20dp"
android:layout_marginBottom="160dp"
android:orientation="vertical" >
<ImageButton
android:id="#+id/seat_updown"
android:layout_width="120dp"
android:layout_height="140dp"
android:scaleType="fitCenter"
android:src="#drawable/doublearrow"
android:rotation="90" /> />
</LinearLayout>
Because the ImageButton was imbedded in the LinearLayout it disappeared!
For button drag & drop I share my code here check it.
Here button touch listener.
MultiTouchListener touchListener = new MultiTouchListener(this);
onButton.setOnTouchListener(touchListener);
Touch listener class.
public class MultiTouchListener implements OnTouchListener {
private float mPrevX;
private float mPrevY;
public MainActivity mainActivity;
public MultiTouchListener(MainActivity mainActivity1) {
mainActivity = mainActivity1;
}
#Override
public boolean onTouch(View view, MotionEvent event) {
float currX, currY;
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
mPrevX = event.getX();
mPrevY = event.getY();
break;
}
case MotionEvent.ACTION_MOVE: {
currX = event.getRawX();
currY = event.getRawY();
MarginLayoutParams marginParams = new MarginLayoutParams(
view.getLayoutParams());
marginParams.setMargins((int) (currX - mPrevX),
(int) (currY - mPrevY), 0, 0);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
marginParams);
view.setLayoutParams(layoutParams);
break;
}
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
Sure, you can get these, make sure the views are drawn atleast once before you try to get the positions. You could try to get the positions in onResume() and try these functions
view.getLocationInWindow() or view.getLocationOnScreen()
or if you need something relative to the parent, use
view.getLeft(), view.getTop()
Follow this link for coordinates.
Retrieve the X & Y coordinates of a button in android?
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.
I have a ScrollView and a very long text on TextView and I want to drag to the next/previous text according to the user action like that:
On my xml:
<ScrollView
android:id="#+id/scrollViewTest"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<TextView
android:id="#+id/textViewTest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="very long text" />
</ScrollView>
On the onCreate I implemented the OnTouchListener in order to drag to the next text.
ScrollView sv = (ScrollView) findViewById(R.id.scrollViewTest);
sv.setOnTouchListener(new MyOnTouch());
The OnTouchListener define like that:
public class MyOnTouch implements OnTouchListener {
#Override
public boolean onTouch(View v, MotionEvent event) {
return pageFlip(v, event);
}
public boolean pageFlip(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
break;
case MotionEvent.ACTION_UP:
float currentX = event.getX();
if (startX > currentX + 150 ) {
nextText(v);
}
if (startX < currentX - 150) {
previousText(v);
}
default:
break;
}
return true;
}
}
The problem is when I'm implement it like that I can get to the next/previous text but I can't scroll up and down in order to see the text in the bottom.
Any suggestions?
In method pageFlip change return true; to return false;