I have 2 webviews on one layout.
I want that on touch, the touched view will get bigger (wider) on the expense of the other view, how can I do it?
Thanks!
Subclass WebView:
public class MyWebView extends WebView{
private MyActivity mActivity;
//Call this on each webview in activity's onCreate after
//instantiating the web views. i.e. after setContentView()
//and assigning it to a variable with findViewById() or
//after programmatically creating the web view.
public setActivity(MyActivity activity){
mActivity = activity;
}
#Override
public boolean onTouchEvent(MotionEvent event){
if (event.getAction()==ACTION_POINTER_DOWN && mActivity!=null){
mActivity.onWebviewTouched(self);
}
super.onTouchEvent(event);
}
}
And in your Activity:
public void onWebviewTouched(MyWebView webView) {
if (webView == mWebviewLeft){
//grow left webview if it isn't already grown, and shrink right webview.
} else if (webView == mWebviewRight) {
//grow right webview if it isn't already grown, and shrink left webview.
}
}
Use the Animation class and its subclasses to modify width of the views smoothly. If using a LinearLayout, you could set one view's layout weight to a constant value, and simply animate the layout weight of the other view to get bigger or smaller than that constant value.
If you just want an instant change, just set the view widths directly with LayoutParams.
Related
I have ScrollView layout (or other layout type it doesn't mater) which has child views. And this parent layout has paddingLeft and paddingRight. I want to have this padding set for each child, but sometimes I have exception where I want that child to reach edges of the display completely (for example TextView with background color). Is there any way how to allow this to happen? I don't wanna set padding for every single child separately.
You told us what you want: "sometimes a Child shouldn't have to respect its Parent padding", so the special behaviour should occurs on Children.
The solution is to EXTEND Child's main View in which it chooses its MARGIN by default and then create a "Child.setNoMargins()" method that removes them from itself when required.
public static class ExtendedTextView extends TextView {
private boolean mHasNoMargins = false;
public ExtendedTextView(Context context) {
super(context);
}
public void setNoMargins() {
mHasNoMargins = true;
if (!isInLayout() && isAttachedToWindow()) requestLayout();
}
#Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
if (!mHasNoMargins && (params instanceof ViewGroup.MarginLayoutParams)) {
((ViewGroup.MarginLayoutParams)params).setMarginStart(20);
((ViewGroup.MarginLayoutParams)params).setMarginEnd(20);
}
super.setLayoutParams(params);
}
}
In this way all created "ExtendedTextView" have default Left/Right margins until you call "setNoMargins()".
Obliviously my code works only if parent ViewGroup supports LayoutParams having Margins (most of them).
I have a logo view, which is a full screen fragment containing single ImageView.
I have to perform some operations after the logo image is completely visible.
Following code is used to invoke the special task
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ImageView logoImageMaster = new ImageView(getContext());
//logoImageMaster.setImageResource(resID); //even after removing this, i am getting the callback twice
try {
// get input stream
InputStream ims = getActivity().getAssets().open("product_logo.png");
// load image as Drawable
Drawable d = Drawable.createFromStream(ims, null);
// set image to ImageView
logoImageMaster.setImageDrawable(d);
}
catch(IOException ex) {
}
logoImageMaster.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() { //FIXME get called twice. Check this out, no info to distinguish first from second
// Log.e("PANEL", "onGlobalLayout of Logo IV ---------------------------------");
activityInterface.doSpecialLogic();
}
});
return logoImageMaster;
}
My exact problem is, onGlobalLayout is called twice for this view hierarchy.
I know that onGlobalLayout is invoked in performTraversal of View.java hence this is expected.
For my use case of Single parent with Single child view, I want to distinguish the view attributes such that doSpecialLogic is called once[onGlobalLayout is called twice] , after the logo image is completely made visible.
Please suggest some ideas.
OnGlobalLayoutListener gets called every time the view layout or visibility changes. Maybe you reset the views in your doSpecialLogic call??
edit
as #Guille89 pointed out, the two set calls cause onGlobalLayout to be called two times
Anyhow, if you want to call OnGlobalLayoutListener just once and don't need it for anything else, how about removing it after doSpecialLogic() call??
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
//noinspection deprecation
logoImageMaster.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
logoImageMaster.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
activityInterface.doSpecialLogic();
It seems to be called one time for each set done over the imageView
logoImageMaster.setImageResource(resID);
logoImageMaster.setImageDrawable(d);
You should Try using kotlin plugin in android
This layout listener is usually used to do something after a view is measured, so you typically would need to wait until width and height are greater than 0. And we probably want to do something with the view that called it,in your case
Imageview
So generified the function so that it can be used by any object that extends View and also be able to access to all its specific functions and properties from the function
[kotlin]
inline fun <T: View> T.afterMeasured(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
f()
}
}
})
}
[/kotlin]
Note:
make sure that ImageView is described properly in the layout. That is its layout_width and layout_height must not be wrap_content. Moreover, other views must not result in this ImageView has 0 size.
I have a form within a ScrollView. When I tap into an EditText the soft keyboard appears and the ScrollView scrolls the now focused EditText so that it just comes into view.
However, I have hint information just below the EditText that I also would like to show, so the scrolling should go just a bit further up, like this:
The EditText is embedded in a form element and actually I'd like to scroll to the bottom of that. I've checked the source code of ScrollView and it will just scroll to the bottom of the currently focused view. Maybe there's a way to tell the ScrollView that the form element is the currently focused element?
Of course I could write my own ScrollView sub class and override the scroll behavior, but I wonder if there's a more elegant way of doing this.
Any other suggestions (with adjust scrolling with a fixed offset or so) are also appreciated.
I have not really found any way to configure the scrolling behavior of the ScrollView from the outside. So I ended up to define my own sub class of ScrollView:`
/**
* {#link ScrollView} extension that allows to configure scroll offset.
*/
public class ConfigurableScrollView extends ScrollView {
private int scrollOffset = 0;
public ConfigurableScrollView (final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public void setScrollOffset (final int scrollOffset) {
this.scrollOffset = scrollOffset;
}
#Override
protected int computeScrollDeltaToGetChildRectOnScreen (final Rect rect) {
// adjust by scroll offset
int scrollDelta = super.computeScrollDeltaToGetChildRectOnScreen(rect);
int newScrollDelta = (int) Math.signum(scrollDelta) * (scrollDelta + this.scrollOffset);
return newScrollDelta;
}
}
computeScrollDelta(...) is the only protected method that can be targeted for overriding, apart from onSizeChanged(...).
The signum function in the example above ensures that scrolling is only increased, if the ScrollView really thinks that scrolling is necessary (e.g. when keyboard pops up).
I can now set the extra scroll offset once from the outside, as calculated from the height of the hint.
It's not hard to use the extended ConfigurableScrollView instead of the standard ScrollView, I only had to replace the ScrollView XML tag with the FQN of the new class.
Considering that you are using ScrollView you have the possibility to use the method ScrollTo as follow:
scrollView.post(new Runnable() {
#Override
public void run() {
sv.scrollTo(x-value, y-value);
}
});
where the first argument is the scroll value for X, while the second argument is the scroll value for Y. So you just have to set your scrollView offset when the keyboard is displayed.
Hope it helps;)
Layouting in Android is getting me rather perplexed.
I'm slowly implementing a custom ImageView where I'd like to make use of the ZoomButtonsController.
However, I would like to decide where the zoom buttons go in the layout and I can't figure out how to move them from the default bottom center position.
I have been experimenting with layouting simple views such as buttons in the main activity and this seems to be working as I would guess and expect.
In the case of the ZoomButtonsController I would however like to reposition them. I'm using a RelativeLayout as the mail layout and add the ZoomButtonsController within the custom ImageView.
The Activity code
public class ImageViewActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
CustomImageView imageView = new CustomImageView(this);
relativeLayout.addView(imageView);
}
}
The CustomImageView code
public class CustomImageView extends ImageView {
private ZoomButtonsController mZoomButtons;
public CustomImageView(Context context) {
super(context);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
mZoomButtons = new ZoomButtonsController(this);
mZoomButtons.getZoomControls();
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
setLayoutParams(layoutParams);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
Log.d("TAG", "touch");
mZoomButtons.setVisible(true);
return true;
}
}
I've tested with WRAP_CONTENT in the parameters, but this only makes the zoom buttons disappear.
As a matter of fact, I couldn't position the ZoomButtonsController in any way and in the end had to accept the default placement.
I've put a WebView loading an image inside a ViewPager. When I try to scroll the image horizontally I move over to the next view instead of scrolling the image.
Is it possible to make it scroll to the end of the image before moving over to the next view?
#Override
public Object instantiateItem(View view, int i) {
WebView webview = new WebView(view.getContext());
webview.setHorizontalScrollBarEnabled(true);
webview.loadUrl("http://www.site.with.an/image.gif");
((ViewPager) view).addView(webview, 0);
return webview;
}
The following is a real working solution which will scroll the WebView on a horizontal swipe as long as it can scroll. If the WebView cannot further scroll, the next horizontal swipe will be consumed by the ViewPager to switch the page.
Extending the WebView
With API-Level 14 (ICS) the View method canScrollHorizontally() has been introduced, which we need to solve the problem. If you develop only for ICS or above you can directly use this method and skip to the next section. Otherwise we need to implement this method on our own, to make the solution work also on pre-ICS.
To do so simply derive your own class from WebView:
public class ExtendedWebView extends WebView {
public ExtendedWebView(Context context) {
super(context);
}
public ExtendedWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean canScrollHor(int direction) {
final int offset = computeHorizontalScrollOffset();
final int range = computeHorizontalScrollRange() - computeHorizontalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
}
Important: Remember to reference your ExtendedWebView inside your layout file instead of the standard WebView.
Extending the ViewPager
Now you need to extend the ViewPager to handle horizontal swipes correctly. This needs to be done in any case -- no matter whether you are using ICS or not:
public class WebViewPager extends ViewPager {
public WebViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
if (v instanceof ExtendedWebView) {
return ((ExtendedWebView) v).canScrollHor(-dx);
} else {
return super.canScroll(v, checkV, dx, x, y);
}
}
}
Important: Remember to reference your WebViewPager inside your layout file instead of the standard ViewPager.
That's it!
Update 2012/07/08: I've recently noticed that the stuff shown above seems to be no longer required when using the "current" implementation of the ViewPager. The "current" implementation seems to check the sub views correctly before capturing the scroll event on it's own (see canScroll method of ViewPager here). Don't know exactly, when the implementation has been changed to handle this correctly -- I still need the code above on Android Gingerbread (2.3.x) and before.
Although Sven mentioned for layout file I want to add detail. After you extend Webview and ViewPager classes,
Inside your activity you will cast to your extended class like this:
web = (MyWebView) findViewById(R.id.webview);
Inside your layout file like this:
<your.package.name.MyWebView
android:id="#+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>