I created a test MvxFrameLayout derived class, which I want to draw a child at 0,0 with size 24x24:
public class MyCustomLayout : MvxFrameLayout
{
public MyCustomLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public MyCustomLayout(Context context, IAttributeSet attrs, IMvxAdapterWithChangedEvent adapter) : base(context, attrs)
{
}
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
if (!changed)
{
return;
}
for (int i = 0; i < this.ChildCount; ++i)
{
var child = this.GetChildAt(i);
child.Layout(0, 0, 24, 24);
}
}
}
Which is used in an activity layout (FirstView.axml) like this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<hotspotappandroid.droid.views.MyCustomLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="ItemsSource Hotspots"
local:MvxItemTemplate="#layout/hotspot" />
</FrameLayout>
The view model which has one item:
public class FirstViewModel : MvxViewModel
{
public string[] Hotspots { get; private set; }
public FirstViewModel()
{
this.Hotspots = new string[] { "A" };
}
}
The hotspot.xml is an ImageView with an image (circle.png) of 24x24:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/circle" />
The problem is the circle image is not being drawn.
If in hotspot.xml I change android:layout_width="fill_parent" and android:layout_height="fill_parent to wrap_content, the image is drawn, but not correctly.
The image is drawn in half. It looks like the image is drawn scaled to double of its size and it's cropped by half (probably due to `child.Layout(0, 0, 24, 24)).
I am not sure what is going on. I see that the child in OnLayout is of type cirrious.mvvmcross.binding.droid.views.MvxListItemView instead of 'ImageView' because that I would have expected. Maybe that has something to do?
The MvxFrameLayout works by inflating a child MvxListItemView for each child. The MvxListItemView itself inherits from FrameLayout and has default layout parameters.
According to the docs - http://developer.android.com/reference/android/widget/FrameLayout.html - this means each child FrameLayout should be sized:
The size of the FrameLayout is the size of its largest child (plus padding), visible or not (if the FrameLayout's parent permits)
Since you are specifying fill_parent for the ImageView inner grandchildren here, then I guess that is what is causing your inner views to be zero-sized.
In order to give your child frames some size, it might be better to give them ImageView sizes directly in the inner child XML, instead of requesting fill_parent
If you do want to return an ImageView directly and to use that as the Child - without the intermediate MvxListItemView then in the latest MvvmCross source I believe you can:
inherit a class MyImageView from ImageView and add an IMvxListItemView implementation
use a custom Adapter which creates and returns MyImageView in an overridden CreateBindableView - https://github.com/MvvmCross/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Binding.Droid/Views/MvxAdapter.cs#L287
Related
I try to display a bitmap with a specific alpha. The Bitmap is created in ARGB format, for each pixel I set the color with value 0x04FFAAAA.
The display is made in a class that extends GLSurfaceView (the class name is BitmapView).
I have another class RelativeLayoutBitmap (extends RelativeLayout), that creates and addView BitmapView.
In screen.xml
I define a relative layout :
<RelativeLayout
android:id="id"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/overlay_background">
<RelativeLayoutBitmap
android:id="id_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</RelativeLayout>
public RelativeLayoutBitmap(Context context, AttributeSet attrs) {
super(context, attrs);
internalConstruct(context, attrs);
}
public RelativeLayoutBitmap(Context context) {
super(context);
internalConstruct(context, null);
}
private void internalConstruct(Context context, AttributeSet attrs){
this.mContext = context;
this.mAttrs = attrs;
if (null != mBitmapView) {
this.removeView(mBitmapView);
mBitmapView = null;
}
// create the bitmapview
mBitmapView = new BitmapView(mContext, mAttrs);
mBitmapView(getBackground()); //same background for mBitmapView
LayoutParams params = (LayoutParams) this.generateDefaultLayoutParams();
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.MATCH_PARENT;
mBitmapView.setLayoutParams(params);
this.addView(mBitmapView, 0);
}
Over this relativeLayout, I have a relativeLayout with a background 0x4D000000.
This doesn't work, the Bitmap was displayed, but visible; I hoped the bitmap will be display and not visibile because the alpha of each pixel was 0.015f.
If I hide the relativeLayout over (or set background to be transparent), the bitmap was displayed and not visibile (if I change the alpha of each pixel to 1.0 the Bitmap is visible.).
If in my screen.xml, I include the BitmapView:
<RelativeLayout
android:id="id"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/overlay_background">
<BitmapView
android:id="id_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</RelativeLayout>
This works, the Bitmap was displayed and not visible; then if I change the alpha of each pixel to 1.0 the Bitmap is visible.
For my final project, I can't modify the app, the BitmpaView must be included in RelativeLayoutBitmap.
I don't understand why I don't have the comportment I expect when my BitmapView is added in a RelativeLayout.
In RelativeLayoutBitmap, what do I need to set to have the bitmap displayed with the alpha of each pixel?
I'm having trouble with a BottomSheetDialogFragment I implemented some days back in a project I'm in.
What happens is that I have a BottomSheet which contains a SearchView and a Recyclerview. The dialog fragment shows correctly and stuff, all good there.
The problem starts when I use the SearchView to filter the Recyclerview's results since when there's 5 or less results, the keyboard is overlapping the now small Recyclerview.
I want to know if it's possible to keep the BottomSheet height as match_parent or something to fill the window or keep the Recyclerview big enough to avoid the keyboard "messing up" with the results. I use the following method to make the fragment expanded when it opens:
private fun expandBottomSheet() {
view?.viewTreeObserver?.addOnGlobalLayoutListener {
val dialog = dialog as BottomSheetDialog
val bottomSheet = dialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
val behavior = BottomSheetBehavior.from<View>(bottomSheet)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
And my XML for the sheet is this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="#string/bottom_sheet_behavior">
<View
android:layout_width="52dp"
android:layout_height="7dp"
android:layout_gravity="center"
android:layout_marginTop="#dimen/size_small_4"
android:layout_marginBottom="#dimen/size_small_4"
android:background="#drawable/border_top_swipe_indicator" />
<androidx.appcompat.widget.SearchView
android:id="#+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:iconifiedByDefault="false"
app:queryHint="#string/text_type_your_query" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/border_top_white"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="#layout/list_item" />
</LinearLayout>
Thanks in advance for the help!
Edit:
The bottom sheet containing the Recyclerview and stuff is a child fragment (a fragment instantiated from another fragment.)
For anyone who is searching for this question, if you want to force your BottomSheetDialogFragment have full screen height, just wrap your bottom sheet content layout inside of this custom FrameLayout:
public class MatchParentFrameLayout extends FrameLayout {
public MatchParentFrameLayout(#NonNull Context context) {
super(context);
}
public MatchParentFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public MatchParentFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MatchParentFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
In the manifest.xml file you have your activities declared there for your application. Inside of the <activity> block where this bottom sheet is hosted you can declare a window soft input mode so that the keyboard does not overlap the view - instead it pushes it up.
<activity
...
android:windowSoftInputMode="adjustResize|stateVisible"> ... </activity>
stateVisible: "The soft keyboard is visible when that's normally appropriate (when the user is navigating forward to the activity's main window)."
adjustResize: "The activity's main window is always resized to make room for the soft keyboard on screen."
Docs
That should work for you. It could be possible, depending on your views, for that to result in a poor UI and UX. If that is true, you could set a focus listener on the search view and, when it gains focus, programmatically set the state of the bottom sheet to expanded. See the answerhere.
Even though I'm not satisfied with the fix I came up to, I have to say it's working smoothly.
Basically I wrapped my bottomsheet with a ViewPager and it's not resizing.
I admit this is a hack and I'm hoping someone can provide a more decent answer to this. In the meantime, ViewPager with a single bottomsheet is the way to go.
You can set the height with the below code in On Activity created
view?.viewTreeObserver?.addOnGlobalLayoutListener {
val rect = Rect()
view?.getWindowVisibleDisplayFrame(rect)
val screenHeight = view?.rootView?.height
val keyPadHeight = screenHeight?.minus(rect.bottom)
if (screenHeight != null) {
if (keyPadHeight != 0) {
if (view?.paddingBottom != keyPadHeight) {
view?.setPadding(0, 0, 0, keyPadHeight!!)
}
} else {
if (view?.paddingBottom != 0) {
view?.setPadding(0, 0, 0, 0)
}
}
Hope this will work for you.
I am creating my own custom viewgroup in android. I have two children. One is a linearLayout (it's the first child and covers half of the screen) with some background image and buttons over it and other is a extension of View (it's the second child and covers whole screen) where I draw something using my finger.
I want the (first child) Linear Layout to be hidden under the (second child) extension of view so that I can use some gesture to swipe the second child to the right hand side (kind of like slide of google,youtube) and see the first child (LinearLayout). The problem is inside onLayout I place the children in certain order but the first child (LinearLayout) always comes in front no matter what I do.
secondchild.layout(0,0,top,bottom);
firstchild.layout(0,0,top/2,bottom);
I also tried
firstchild.layout(0,0,top/2,bottom);
secondchild.layout(0,0,top,bottom);
But the first child always comes on top.
Code for Costume ViewGroup:
public class RootViewLayout extends ViewGroup {
private View mDrawView;
private View mSlideView;
private int mTop;
private int mDragRange;
public RootViewLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
mDrawView = findViewById(R.id.content_frame_white);
mSlideView = findViewById(R.id.slide_frame);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom){
bringChildToFront(mDrawView);
mDrawView.layout(0, 0, right, bottom);
mSlideView.layout(0, 0, right/2, bottom);
}
}
XML Code :
<com.example.drawapp.RootViewLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/Root_View_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<com.example.drawapp.DrawView
android:id="#+id/content_frame_white"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/whitepaperwithcoffeestain">
</com.example.drawapp.DrawView>
<LinearLayout
android:id="#+id/slide_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#drawable/slidebackgrd"
android:orientation="vertical">
<Button
android:id="#+id/pen"
android:layout_width="100dip"
android:layout_height="wrap_content"
android:background="#drawable/pic"/>
</LinearLayout>
</com.example.drawapp.RootViewLayout>
When I don't put
bringChildToFront(mDrawView);
the blank is placed with proper black background, but that's not what I actually want. I want whole screen to be covered with DrawView (whose background is white with coffee stain over it).
Is there any specific way to tell the children to be placed one on top of other?
You need to change the z-order of the child views. You should probably use bringChildToFront(), i.e.
parentLayout.bringChildToFront(secondChild);
However, the effect depends on the type of the parent layout (e.g. if it's a LinearLayout then the views would be swapped). Since you're overlaying I guess it means it's a RelativeLayout, and then it should work as you want.
I see that in your case you're using a custom ViewGroup. If it's only to achieve the "full width/half width" children, then I would suggest swapping it for a RelativeLayout. Add secondchild with match_parent and firstchild as right of a centered 0dp view as in Adjust width to half screen
Or another option, possibly simpler, is to just change the visibility on the child that goes on top (VISIBLE or GONE).
I am trying to create a custom ViewGroup, and I want to use it with a full screen application. I am using the "requestWindowFeature(Window.FEATURE_NO_TITLE)" to hide the title bar. The title bar is not showing, but it still consuming space on top of the window.
The image above was generated with the following code:
public class CustomLayoutTestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Button b = new Button(this);
b.setText("Hello");
CustomLayout layout = new CustomLayout(this);
layout.addView(b);
setContentView(layout);
}
}
public class CustomLayout extends ViewGroup {
public CustomLayout(Context context) {
super(context);
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("CustomLayout", "changed="+changed+" l="+l+" t="+t+" r="+r+" b="+b);
final int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
final View v = getChildAt(i);
v.layout(l, t, r, b);
}
}
}
(The full Eclipse project is here)
It is interesting to see that it is the Android that is given this space for my custom layout. I am setting the CustomLayout as the root layout of my Activity. In the Log in the "onLayout" is receiving "t=25", and that is what is pushing my layout down. What I don't know is what I am doing wrong that makes Android the "t=25" (which is exactly the height of the title bar).
I am running this code in the Android SDK 2.1, but I also happens in Android 2.2.
EDIT: If I change the CustomLayout class for some default layout (such as LinearLayout), the space disappears. Of course, the default layouts of Android SDK don't create the layout I am trying to create, so that is why I am creating one.
Although the layout I am creating is somewhat complex, this is the smallest code I could create reproducing the problem I have with my layout.
It's not a full answer, but in the meantime you can work around the problem by wrapping your custom layout in a <FrameLayout />
Also, it's worth noting that your layout extends beyond the bottom of the screen. It's shifted down by the title bar height (38 pixels in my emulator)
Edit: Got it. onLayout() (and the corresponding layout() method) specify that the coordinate are not relative to the screen origin, they're relative to the parent ( http://developer.android.com/reference/android/view/View.html#layout%28int,%20int,%20int,%20int%29 ). So the system is telling you that you're at relative coordinates (0, 38), and you're adding it when passing that down to your child, which means that you're saying that your child is at screen coordinates (0, 76), causing the gap.
What you actually want to do is:
v.layout(0, 0, r - l, b - t);
That will put your child Views aligned with the top left corner of your View, with the same width and height as your view.
I had the same issue with a FrameLayout in 2.2
I fixed it by adding android:layout_gravity="top" to the FrameLayout
I'm trying to create a custom Android control that contains a LinearLayout. You can think of it as an extended LinearLayout with fancy borders, a background, an image on the left...
I could do it all in XML (works great) but since I have dozens of occurences in my app it's getting hard to maintain. I thought it would be nicer to have something like this:
/* Main.xml */
<MyFancyLayout>
<TextView /> /* what goes inside my control's linear layout */
</MyfancyLayout>
How would you approach this? I'd like to avoid re-writing the whole linear layout onMeasure / onLayout methods. This is what I have for the moment:
/* MyFancyLayout.xml */
<TableLayout>
<ImageView />
<LinearLayout id="container" /> /* where I want the real content to go */
</TableLayout>
and
/* MyFancyLayout.java */
public class MyFancyLayout extends LinearLayout
{
public MyFancyLayout(Context context) {
super(context);
View.inflate(context, R.layout.my_fancy_layout, this);
}
}
How would you go about inserting the user-specified content (the TextView in main.xml) in the right place (id=container)?
Cheers!
Romain
----- edit -------
Still no luck on this, so I changed my design to use a simpler layout and decided to live with a bit of repeated XML. Still very interested in anyone knows how to do this though!
This exact question bugged me for some time already but it's only now that I've solved it.
From a first glance, the problem lies in the fact that a declarative content (TextView in Your case) is instantiated sometime after ctor (where we're usually inflating our layouts), so it's too early have both declarative and template content at hand to push the former inside the latter.
I've found one such place where we can manipulate the both: it's a onFinishInflate() method. Here's how it goes in my case:
#Override
protected void onFinishInflate() {
int index = getChildCount();
// Collect children declared in XML.
View[] children = new View[index];
while(--index >= 0) {
children[index] = getChildAt(index);
}
// Pressumably, wipe out existing content (still holding reference to it).
this.detachAllViewsFromParent();
// Inflate new "template".
final View template = LayoutInflater.from(getContext())
.inflate(R.layout.labeled_layout, this, true);
// Obtain reference to a new container within "template".
final ViewGroup vg = (ViewGroup)template.findViewById(R.id.layout);
index = children.length;
// Push declared children into new container.
while(--index >= 0) {
vg.addView(children[index]);
}
// They suggest to call it no matter what.
super.onFinishInflate();
}
A labeled_layout.xml referenced above is not unlike something like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation ="vertical"
android:layout_width ="fill_parent"
android:layout_height ="wrap_content"
android:layout_marginLeft ="8dip"
android:layout_marginTop ="3dip"
android:layout_marginBottom ="3dip"
android:layout_weight ="1"
android:duplicateParentState ="true">
<TextView android:id ="#+id/label"
android:layout_width ="fill_parent"
android:layout_height ="wrap_content"
android:singleLine ="true"
android:textAppearance ="?android:attr/textAppearanceMedium"
android:fadingEdge ="horizontal"
android:duplicateParentState="true" />
<LinearLayout
android:id ="#+id/layout"
android:layout_width ="fill_parent"
android:layout_height ="wrap_content"
android:layout_marginLeft ="8dip"
android:layout_marginTop ="3dip"
android:duplicateParentState="true" />
</LinearLayout>
Now (still omitting some details) elsewhere we might use it like this:
<com.example.widget.LabeledLayout
android:layout_width ="fill_parent"
android:layout_height ="wrap_content">
<!-- example content -->
</com.example.widget.LabeledLayout>
This approach saves me a lot of code! :)
As esteewhy explains, just swap the xml-defined contents into where you want them internally in your own layout, in onFinishInflate(). Example:
I take the contents that I specify in the xml:
<se.jog.custom.ui.Badge ... >
<ImageView ... />
<TextView ... />
</se.jog.custom.ui.Badge>
... and move them to my internal LinearLayout called contents where I want them to be:
public class Badge extends LinearLayout {
//...
private LinearLayout badge;
private LinearLayout contents;
// This way children can be added from xml.
#Override
protected void onFinishInflate() {
View[] children = detachChildren(); // gets and removes children from parent
//...
badge = (LinearLayout) layoutInflater.inflate(R.layout.badge, this);
contents = (LinearLayout) badge.findViewById(R.id.badge_contents);
for (int i = 0; i < children.length; i++)
addView(children[i]); //overridden, se below.
//...
super.onFinishInflate();
}
// This way children can be added from other code as well.
#Override
public void addView(View child) {
contents.addView(child);
}
Combined with custom XML attributes things gets very maintainable.
You can create your MyFancyLayout class by extending LinearLayout. Add the three constructors which call a method ("initialize" in this case) to set up the rest of the Views:
public MyFancyLayout(Context context) {
super(context);
initialize();
}
public MyFancyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public MyFancyLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
Within initialize, you do anything you need to to add the extra views. You can get the LayoutInflater and inflate another layout:
final LayoutInflater inflator = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflator.inflate(R.layout.somecommonlayout, this);
Or you can create Views in code and add them:
ImageView someImageView = new ImageView(getContext());
someImageView.setImageDrawable(myDrawable);
someImageView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
addView(someImageView);
If you're going to use the Context a lot, you can store a reference to it in your constructors and use that rather than getContext() to save a little overhead.
just use something like this:
<org.myprogram.MyFancyLayout>
...
</org.myprogram.MyFancyLayout>
Useful link - http://www.anddev.org/creating_custom_views_-_the_togglebutton-t310.html