I have a LinearLayout inside a ScrollView. The LinearLayout has 100 FrameLayouts. When a button is clicked I insert new FrameLayouts in an in-between position from a BackgroundWorker. In order to maintain the position of the currently viewed FrameLayout I scroll the ScrollView to the offset that is equal to the current offset plus the height of the newly inserted FrameLayout.
While this process is running in the BackgroundWorker if the user flings the contents of the ScrollView with a positive velocity the same FrameLayout is coming again and again. For instance you start flinging with a small velocity from FrameLayout numbered 7, 8 appears from bottom but before appears fully again 7 comes. User scroll also works fine. The problem is with only fling.
I found that while flinging, in the OnScrollChanged event the oldt parameter is greater than t which is unexpected because the fling velocity is positive.
Below I insert new FrameLayouts from the 5th, 6th, 7th, 8th,.... positions in order.
public class MainActivity : Activity {
ScrollView1 sV;
Button btn;
LinearLayout linearLayout;
BackgroundWorker worker;
Handler handler;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
sV = FindViewById<ScrollView1>(Resource.Id.scrollView1);
btn = FindViewById<Button>(Resource.Id.pagedownbutton);
btn.Click += Btn_Click;
sV.SetBackgroundColor(Android.Graphics.Color.White);
linearLayout = new LinearLayout(ApplicationContext);
linearLayout.Orientation = Orientation.Vertical;
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
handler = new Handler();
for (int i = 0; i < 100; i++) {
TextView txt = new TextView(ApplicationContext);
txt.Text = i.ToString();
txt.TextSize = 50;
txt.SetTextColor(Color.White);
txt.Gravity = Android.Views.GravityFlags.Center;
FrameLayout frameLayout = new FrameLayout(ApplicationContext);
frameLayout.SetBackgroundColor(Android.Graphics.Color.Blue);
frameLayout.AddView(txt);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(700, 1200);
layoutParams.SetMargins(0, 5, 0, 0);
layoutParams.Gravity = Android.Views.GravityFlags.Center;
frameLayout.LayoutParameters = layoutParams;
linearLayout.AddView(frameLayout, i);
}
sV.AddView(linearLayout);
}
private void Worker_DoWork(object sender, DoWorkEventArgs e) {
for (int i = 0; i < 1000; i++) {
TextView txt = new TextView(ApplicationContext);
txt.Text = "i" + i.ToString();
txt.TextSize = 50;
txt.SetTextColor(Color.White);
txt.Gravity = Android.Views.GravityFlags.Center;
FrameLayout frameLayout = new FrameLayout(ApplicationContext);
frameLayout.SetBackgroundColor(Android.Graphics.Color.Blue);
frameLayout.AddView(txt);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(700, 1200);
layoutParams.SetMargins(0, 5, 0, 0);
layoutParams.Gravity = Android.Views.GravityFlags.Center;
frameLayout.LayoutParameters = layoutParams;
//Inducing some delay so that the background process will run long
for (int j = 0; j < 10000000; j++) {
}
//Adding FrameLayout and scrolling
handler.Post(() => {
linearLayout.AddView(frameLayout, 5 + i);
if (sV.ScrollY >= (5 + i) * 1205)
sV.ScrollTo(0, 1205 + sV.ScrollY);
});
}
}
private void Btn_Click(object sender, System.EventArgs e) {
worker.RunWorkerAsync();
}
}
public class ScrollView1 : ScrollView{
public ScrollView1(Context context):base(context) {
}
public ScrollView1(Android.Content.Context context, Android.Util.IAttributeSet attr) : base(context, attr) {
}
protected override void OnScrollChanged(int l, int t, int oldl, int oldt) {
}
public override void Fling(int velocityY) {
base.Fling(velocityY);
}
}
How can I make the FrameLayouts appear in order without scroll malfunctions while flinging with the programmatic scroll takes place in a background thread?
Related
I've created a RelativeLayout within my activity, and I'm dynamically creating ImageView objects that I'm adding to the RelativeLayout using addView along with RelativeLayout.LayoutParams. When I then go to get the position of the child view, it always returns the coordinates of the parent view. Here are the pertinent parts of the code:
public class MainActivity extends AppCompatActivity {
public RelativeLayout parentLayout;
RelativeLayout.LayoutParams layoutParams;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
parentLayout = findViewById(R.id.parentRelativeLayout);
}
// Method to dynamically add the ImageView to the parent
public int addObject(Drawable drawing, int x, int y) {
ImageView newObj = new ImageView(getApplicationContext());
newObj.setBackground(drawing);
layoutParams = new RelativeLayout.LayoutParams(50, 50);
layoutParams.leftMargin = x;
layoutParams.topMargin = y;
int childIndex = parentLayout.getChildCount()-1;
parentLayout.addView(newObj, childIndex,layoutParams);
return childIndex;
}
public int[] getChildLocation(int childIndex) {
View childView;
int[] outlineCoord = new int[2];
childView = parentLayout.getChildAt(childIndex);
childView.getLocationOnScreen(outlineCoord);
return outlineCoord;
}
}
If I then add a child and attempt to retrieve the coordinates of the child view, I always get the location of the parent:
childIndex = addObject(getResources().getDrawable(blackcircle), 100, 200);
outlineCoord = getChildLocation(childIndex);
Log.i("LOC:", "Location of child is X: " + outlineCoord[0] + " Y: " + outlineCoord[1]);
I can't quite figure out what I'm doing wrong here.
I have a constraintLayout that contains multiple nodeView's. A nodeView is a ImageView line attached to the left side of a ImageView circle
I now want to connect X amount of nodes together. To programmatically set constraints, you use the R.id, but since I'm connecting multiple same nodes together, and they all share the same R.id, this isn't working. Is there any way to reference a specific view's ImageView as a reference for setting a constraint for another ImageView? I'm starting to think I'm approaching this the wrong way entirely. Thanks.
EDIT: Here is the rest of the code.
node code
private void init(Context context, AttributeSet attrs, String description, boolean active, boolean base) {
View inflatedView = inflate(context, R.layout.tracking_node, this);
nodeLine = inflatedView.findViewById(R.id.imageNodeLine);
nodeImage = inflatedView.findViewById(R.id.imageNode);
nodeText = inflatedView.findViewById(R.id.textNode);
nodeLine.setId(View.generateViewId());
nodeImage.setId(View.generateViewId());
nodeText.setText(description);
if (active){
nodeImage.setImageResource(R.drawable.circle_green);
nodeLine.setImageResource(R.color.support_success);
}else{
nodeImage.setImageResource(R.drawable.circle_grey);
nodeImage.setImageResource(R.color.grey);
}
//Remove left-side connecting line if base node
if (base){
nodeLine.getLayoutParams().width = 20;
nodeLine.setImageResource(R.color.transparent);
}
}
public int getNodeImageId(){
return nodeImage.getId();
}
public int getNodeLineId(){
return nodeLine.getId();
}
constraintLayout code
private void init(Context context, AttributeSet attrs) {
View inflatedView = inflate(context, R.layout.delivery_status_view, this);
deliveryTrackerView = inflatedView.findViewById(R.id.linearLayoutDeliveryTracking);
shippingDetailsButton = inflatedView.findViewById(R.id.btnShippingDetails);
//steps[] is a string array that contains the content of each node
DeliveryNodeView node = new DeliveryNodeView(context, attrs, steps[0], true, true);
//Saves resource ID of last node image
int pastNodeID = node.getNodeImageId();
//Generates nodes
for (int i = 1; i < steps.length; i++){
boolean active = ((i + 1) / currentStep) <= 1;
node = new DeliveryNodeView(context, attrs, steps[i], active, false);
int nodeLineID = node.getNodeLineId();
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(deliveryTrackerView);
deliveryTrackerView.addView(node);
constraintSet.connect(nodeLineID, ConstraintSet.START, pastNodeID, ConstraintSet.END);
pastNodeID = node.getNodeImageId();
}
}
There are a few problems with your code. Here is some sample code that builds a 5x5 colored box array that looks like this:
The comments in the code outline the key steps. activity_main.xml is just an empty ConstraintLayout.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ConstraintLayout layout = findViewById(R.id.layout);
int colorCounter = 0;
int idToTop = ConstraintSet.PARENT_ID;
int idToTopSide = ConstraintSet.TOP;
for (int i = 0; i < 5; i++) {
int idToLeft = ConstraintSet.PARENT_ID;
int idToLeftSide = ConstraintSet.START;
for (int j = 0; j < 5; j++) {
View box = getBox(colorCounter++ % 2 == 0);
// Add the view before getting the ConstraintSet.
layout.addView(box);
ConstraintSet cs = new ConstraintSet();
cs.clone(layout);
// Must constrain the view horizontally...
cs.connect(box.getId(), ConstraintSet.START, idToLeft, idToLeftSide);
//... and vertically.
cs.connect(box.getId(), ConstraintSet.TOP, idToTop, idToTopSide);
idToLeft = box.getId();
idToLeftSide = ConstraintSet.END;
// Apply the ConstraintSet to the layout.
cs.applyTo(layout);
}
idToTop = idToLeft;
idToTopSide = ConstraintSet.BOTTOM;
}
}
private View getBox(boolean isRed) {
View view = new View(this);
view.setId(View.generateViewId());
view.setBackgroundColor((isRed) ? Color.RED : Color.BLUE);
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(200, 200);
view.setLayoutParams(lp);
return view;
}
}
Alternate code with the same result that separates out the view creation from making of the ConstraintSet connections. This may be a little more efficient.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ConstraintLayout layout = findViewById(R.id.layout);
int colorCounter = 0;
int[][] connections = new int[5][5];
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
View box = getBox(colorCounter++ % 2 == 0);
// Add the view before getting the ConstraintSet.
layout.addView(box);
connections[row][col] = box.getId();
}
}
int idToTop = ConstraintSet.PARENT_ID;
int idToTopSide = ConstraintSet.TOP;
ConstraintSet cs = new ConstraintSet();
cs.clone(layout);
for (int i = 0; i < 5; i++) {
cs.connect(connections[i][0], ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
cs.connect(connections[i][0], ConstraintSet.TOP, idToTop, idToTopSide);
for (int j = 1; j < 5; j++) {
// Must constrain the view horizontally...
cs.connect(connections[i][j], ConstraintSet.START, connections[i][j - 1], ConstraintSet.END);
//... and vertically.
cs.connect(connections[i][j], ConstraintSet.TOP, idToTop, idToTopSide);
// Apply the ConstraintSet to the layout.
}
idToTop = connections[i][0];
idToTopSide = ConstraintSet.BOTTOM;
}
cs.applyTo(layout);
}
private View getBox(boolean isRed) {
View view = new View(this);
view.setId(View.generateViewId());
view.setBackgroundColor((isRed) ? Color.RED : Color.BLUE);
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(200, 200);
view.setLayoutParams(lp);
return view;
}
}
I want to add new child views after every 50 pixels of scrolling but the statement Log.d("scroll",scrollY+":"+oldScrollY); is missing some values i.e. scrollY = 149 and scrollY = 151 are there in Log but scrollY = 150 is missing. So the code doesn't run for scrollY = 150 and new child views is not added.
Although it works as it should for most of the values of scrollY but for few values of scrollY, the event is not being triggered.
How can i resolve this issue? Or is there any other way to achieve this.
public class MainActivity extends AppCompatActivity implements ScrollViewListener {
RelativeLayout parent;
TextView height;
ObservableScrollView scrollView;
int position=1;
View view1,view2;
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
parent = (RelativeLayout) findViewById(R.id.parent);
scrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
LayoutInflater inflater = LayoutInflater.from(this);
view1 = inflater.inflate(R.layout.education,null, false);
view1.setId(R.id.parent+position);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.LEFT_OF,R.id.divider);
params.topMargin = 100;
params.rightMargin = 3;
parent.addView(view1, params);
position++;
final Animation leftAnimation = new TranslateAnimation(view1.getX()-50,view1.getX(),view1.getY()+100,view1.getY());
leftAnimation.setDuration(1000);
view1.setAnimation(leftAnimation);
scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
Log.d("scroll",scrollY+":"+oldScrollY);
if((scrollY-oldScrollY)>0 && scrollY%50 == 0)
{
Log.d("scroll","abcghfg");
if(position%2==0)
{
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.RIGHT_OF,R.id.divider);
// params.topMargin = (position-1)*(view1.getLayoutParams().height)+position*100;
params.topMargin = 30;
params.leftMargin = 3;
params.addRule(RelativeLayout.BELOW,parent.getChildAt(position-1).getId());
Log.d("scroll","abc");
view2 = LayoutInflater.from(MainActivity.this).inflate(R.layout.education,null,false);
view2.setId(R.id.parent+position);
Animation rightAimation = new TranslateAnimation(view2.getX()+50,view2.getX(),view2.getY()+100,view2.getY());
rightAimation.setDuration(1000);
view2.startAnimation(rightAimation);
parent.addView(view2, params);
}
else
{
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.LEFT_OF,R.id.divider);
//params.topMargin = (position-1)*(view1.getLayoutParams().height)+position*100;
params.topMargin = 30;
params.rightMargin = 3;
params.addRule(RelativeLayout.BELOW,parent.getChildAt(position-1).getId());
Log.d("scroll","def");
view2 = LayoutInflater.from(MainActivity.this).inflate(R.layout.education,null,false);
view2.setId(R.id.parent+position);
Animation animation = new TranslateAnimation(view2.getX()-50,view2.getX(),view2.getY()+100,view2.getY());
animation.setDuration(1000);
view2.startAnimation(animation);
parent.addView(view2, params);
}
position++;
}
}
});
}
}
Thanks in advance.
Android doesn't render every pixel in a scroll, because it would be bad for performance. If you scroll really slow, you will see that your callback will be called for every pixel, but if you scroll fast, it will be called just when the render triggers.
In your case, you need to check if it rendered 50 or more pixels since last time you rendered the last view.
I have created a GridView control, which inhertis from a ScrollView, the idea of this control, is that it will contain multiple Views arranged in a grid format with a given number of columns and rows.
When the view is first built, the GridView doesn't know the size of its container, so I wait until the onSizeChanged method is called, then I apply the relevant sizing.
When the below is run, it doesn't re-size the grid to show it correctly, each control is only as big as it needs to be to show the text.
When the `onSizeChanged' method is called, it has the correct size, and applies the correct size to each child view, but it doesn't affect the way the controls are drawn (i.e. they're still all bunched up on the top left of the screen).
Despite this, I have actually got it working, but it draws twice. I do this by creating a Runnable which calls ResizeList. Then calling new Handler().post(r) straight after I call BuildIt.
Although this is a solution, I just don't understand why it doesn't work in the below form.
Incidentally, if the GridView is the main View added to the Activity, it displays fine, it's only when it's subsequently added. Which is why I have the Button, which you have to press to show the grid.
Can anyone suggest why the below code doesn't work properly?
public class MainActivity extends Activity {
GridView sv;
FrameLayout flay;
#Override protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
flay=new FrameLayout(this);
this.setContentView(flay);
Button b=new Button(this);
b.setText("press me to show grid view");
b.setOnClickListener(ocl);
flay.addView(b);
}
OnClickListener ocl=new OnClickListener()
{
#Override public void onClick(View v)
{
BuildIt();
}};
private void BuildIt()
{
flay.removeAllViews(); // remove the button control
sv=new GridView(this);
for (int c1=0 ; c1<30 ; c1++)
{
TextView tv=new TextView(this);
tv.setText("Item "+c1);
tv.setGravity(android.view.Gravity.CENTER);
sv.addListItem(tv);
}
flay.addView(sv);
sv.ConstructList();
}
}
The GridView class
public class GridView extends ScrollView
{
final int rows=4;
final int cols=4;
private ArrayList<View> allViews=new ArrayList<View>();
private LinearLayout ll;
public GridView(Context context)
{
super(context);
ll=new LinearLayout(context);
ll.setOrientation(LinearLayout.VERTICAL);
this.addView(ll);
}
public void addListItem(View v)
{
allViews.add(v);
}
public void ConstructList()
{
int c1=0;
ll.removeAllViews(); // Just in case we're re-building
LinearLayout row=null;
for (View v : allViews)
{
if (c1%cols==0)
{
row=new LinearLayout(this.getContext());
ll.addView(row);
}
row.addView(v);
c1++;
}
}
private void ResizeList()
{
int useHeight=getHeight()/rows;
int useWidth=getWidth()/cols;
LinearLayout.LayoutParams lpCol=new LinearLayout.LayoutParams(useWidth, useHeight);
Log.i("log","About to set width/height="+useWidth+"/"+useHeight);
int numKids= ll.getChildCount();
for (int c1=0 ; c1<numKids ; c1++)
{
LinearLayout ll2=(LinearLayout)ll.getChildAt(c1);
for (int c2=0 ; c2<ll2.getChildCount() ; c2++) // use getChildCount rather than cols, just in case it's the last one
{
View v=ll2.getChildAt(c2);
v.setLayoutParams(lpCol);
}
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
ResizeList();
}
}
I have a function which is used to resize the child's width and height in gridView.
May be this could help you :
public static void setGridChild_Height(GridView gridView, int columns) {
ListAdapter listAdapter = gridView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
int items = listAdapter.getCount();
int rows = 0;
for (int j = 0; j < items; j++) {
View view = gridView.getChildAt(j);
if (view != null && view.getHeight() > totalHeight) {
totalHeight = view.getHeight();
}
}
System.out.println("totalHeight -> " + totalHeight);
if (totalHeight > 0) {
for (int j = 0; j < items; j++) {
View view = gridView.getChildAt(j);
if (view != null && view.getHeight() < totalHeight) {
view.setMinimumHeight(totalHeight);
}
}
}
// View listItem = listAdapter.getView(0, null, gridView);
// listItem.measure(0, 0);
// totalHeight = listItem.getMeasuredHeight();
//
// float x = 1;
// if (items > columns) {
// x = items / columns;
// rows = (int) (x + 1);
// totalHeight *= rows;
// }
//
// ViewGroup.LayoutParams params = gridView.getLayoutParams();
// params.height = totalHeight;
// gridView.setLayoutParams(params);
}
Try to change logic as per your requirement.
Code is not tested perfectly.
It's because onSizeChanged when newly added to the view hierarchy uses it's old sizes of "0" (according to the docs: http://developer.android.com/reference/android/view/View.html#onSizeChanged(int, int, int, int))
I think what you want is to a addOnLayoutChangedListener : http://developer.android.com/reference/android/view/View.html#addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener)
Using the ViewTreeObserver might be another option that will work for you: How can you tell when a layout has been drawn?
I have TextView created programmatically, like that:
TextView mTextView = new TextView(getApplicationContext());
final LayoutParams params = new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
((MarginLayoutParams) params).setMargins(8, 8, 0, 0);
mTextView.setText(mText);
mTextView.setLayoutParams(params);
mTextView.setPadding(2, 2, 2, 2);
mTextView.setBackground(getResources().getDrawable(R.drawable.note_corps));
tagMap.addView(mTextView);
textViewsWidth = mTextView.getWidth();
But mTextView.getWidth() always returns 0
And if I try:
mTextView.getLayoutParams().width
It returns the LayoutParams corresponding value in the LayoutParams class (-1 or -2)
How can I get the view's width ?
EDIT I need to do this here:
#Override
public void onPostExecute(Hashtable<String, Integer> hash){
final ScrollView tagMapScroll = (ScrollView) findViewById(R.id.tagMapScroll);
final LinearLayout tagMap = (LinearLayout) findViewById(R.id.tagMap);
final ArrayList<Integer> arr = new ArrayList<Integer>(hash.values());
final Enumeration<String> e = hash.keys();
int index = 0;
int textViewsWidth = 0;
while(e.hasMoreElements()){
final TextView tV = new TextView(getApplicationContext());
tV.setTextColor(Color.parseColor("#666666"));
tV.setText(Html.fromHtml(randomColor() + e.nextElement()));
tV.setTextSize(arr.get(index));
final LayoutParams params = new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
((MarginLayoutParams) params).setMargins(8, 8, 0, 0);
tV.setLayoutParams(params);
tV.setPadding(2, 2, 2, 2);
tV.setBackground(getResources().getDrawable(R.drawable.note_corps));
tagMap.addView(tV);
textViewsWidth += tV.getWidth();
index++;
}
tagMapScroll.setVisibility(ScrollView.VISIBLE);
}
EDIT SOLUTION I used this from #androiduser's answer:
mTextView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width = mTextView.getMeasuredWidth();
int height = mTextView.getMeasuredHeight();
Problem solved !
I used this solution:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Ensure you call it only once
yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Here you can get the size :)
}
});
get this way:
tV.post(new Runnable() {
#Override
public void run() {
int width=tV.getMeasuredWidth();
}
});
those values are constant value for FILL_PARENT and WRAP_CONTENT. If you want to check the view size you can try this way:
tagMap.addView(mTextView);
tagMap.post(new Runnable() {
#Override
public void run() {
mTextView. getWidth();
}
});
you have to wait until android draws the TextView. This way you are posting a Runnable in the tagMap queue, that is executed, hopefully after the textview is draw (so it should be weight and height)
In this method you can get the width height of the view..
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
int width = mTextView.getWidth();
int height = mTextView.getHeight();
}
and define TextView mTextView as global
You have to use ViewTreeObserver class which is used to register listeners that can be notified of global changes in the view tree. Such global events include, but are not limited to, layout of the whole tree, beginning of the drawing pass, touch mode change.
In the Activity's onCreate mehotd put this:
ViewTreeObserver vto = mTextView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mTextView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int viewWidth = mTextView.getMeasuredWidth();
}
});