I have a custom layout
public class PersonView extends LinearLayout{
public PersonView(Context context, AttributeSet attrs) {
super(context, attrs);
initUi(context);
}
public PersonView(Context context) {
super(context);
initUi(context);
}
private void initUi(Context context){
LayoutInflater.from(context).inflate(R.layout.person_view, this, true);
profilePicture = (ImageView)findViewById(R.id.profile_picture);
...
}
Layout is defined in xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">
...
Then I use it in other layout
Case 1
// In this case android:layout_margin is specified so it should be used
<my.package.PersonView android:layout_margin="10dp" .../>
Case 2
// In this case android:layout_margin is NOT specified so I want for my PersonView some default value should be used (say 5pt)
<my.package.PersonView .../>
What should I do for my custom layout PersonView to achieve Case 2?
Similar problem has been solved at https://stackoverflow.com/a/25982512/3554436
Add android:layout_margin to attrs.xml
<declare-styleable name="PersonView">
...
<attr name="android:layout_margin" />
...
</declare-styleable>
Access the attribute like other custom attributes:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PersonView);
float margin = a.getDimension(R.styleable.PersonView_android_layout_margin, 0);
...
boolean hasMargin = a.hasValue(R.styleable.PersonView_android_layout_margin);
Related
I have successfully made a custom PinView class.
Now the problem is I want to set the pin view buttons size according to PinView size that is specified in XML. Like a button would have 5% width and height of PinView.
currently, I am setting PinView height and width by doing this getPinPadHeight() is returning 80% of screen height and 70% of screen width
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
pinView.getLayoutParams().height = ViewUtil.getPinPadHeight();
pinView.getLayoutParams().width = ViewUtil.getPinPadWidth();
}
but now the problem is this when I use this custom PinView view in my main activity XML file like this.
<RelativeLayout 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:id="#+id/parent_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary"
android:orientation="vertical"
tools:context=".MainActivity">
<production.kado.lock.views.PinView
android:id="#+id/pinView"
android:layout_centerInParent="true"
android:layout_width="300dp"
android:layout_height="200dp"
app:changePassword="true" />
</RelativeLayout>
and i want to change its height and width its not changing its always set according to screen height and width percentage how can i make this view like some library so i can set height and width according to my preference and views inside it automatically measure themselves accroding to xml attributes when I will use this custom view
public class PinView extends RelativeLayout {
public PinView(Context context) {
super(context);
initView();
}
public PinView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public PinView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
pinView.getLayoutParams().height = ViewUtil.getPinPadHeight();
pinView.getLayoutParams().width = ViewUtil.getPinPadWidth();
}
}
What about doing your calculations inside onSizeChanged?
Add another attribute to Res/values/attrs.xml
<declare-styleable name="PinView">
<attr name="changePassword" format="boolean" />
<attr name="customSize" format="boolean" />
</declare-styleable>
In your constructors with attributes, retrieve the customSize boolean
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PinView, 0, 0);
//Set CustomSize boolean Global Variable
customSize = a.getBoolean(R.styleable.PinView_customSize, false);
then change your onAttachedToWindow method:
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if(!customSize) {
pinView.getLayoutParams().height = ViewUtil.getPinPadHeight();
pinView.getLayoutParams().width = ViewUtil.getPinPadWidth();
}
}
In this way, if app:customSize="true" then it will skip the ViewUtil.getPinPadHeight() and ViewUtil.getPinPadWidth()
I would like to set a custom attribute (in this case, app:unfocusColor) to the value of another attribute (e.g app:cardBackgroundColor), is this possible?
I try the below method, but has error
<android.support.v7.widget.CardView
.....
app:cardBackgroundColor="#android:color/holo_blue_bright"
app:unfocusColor="#{app:cardBackgroundColor}"
>
yes you can!
for example
create res/valuesmyattr.xml
use this code as content
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="myatt">
<attr name="viewName" format="string"/>
</declare-styleable>
</resources>
in your activity use this code
#Override
protected void onCreate(Bundle savedInstanceState) {
getLayoutInflater().setFactory(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
int id =attrs.getAttributeResourceValue("http://schemas.android.com/apk/res/android","id",-1);
if (id == R.id.specialViewId) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myatt);
String viewName = typedArray.getString(R.styleable.myatt_viewName);
}
return super.onCreateView(parent, name, context, attrs);
}
and your view should lookalike this
<TextView
app:number="1"
android:id="#+id/specialViewId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingPrefix"></TextView>
finally if you want use this with out overriding activity you can use LayourInflaterFactory interface
I am trying to use two properties from attrs.xml such as: content and handle. When I use them while building the layout the view goes away. I have been trying the bug for 3 days and nothing worked.
Help is much appreciated!
Code in Context
<hh.hhh.app.android.hhhh.widget.ExpandablePanel
android:id="#+id/expandablePanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:handle="#+id/expandButton"
app:content="#+id/listViewProduct"
app:collapsedHeight="50dip"
app:animationDuration="25">
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/listViewProduct"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/expandButton"/>
</hh.hhh.app.android.hhhh.widget.ExpandablePanel>
attrs.xml code
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ExpandablePanel">
<attr name="handle" format="reference" />
<attr name="content" format="reference" />
<attr name="collapsedHeight" format="dimension"/>
<attr name="animationDuration" format="integer"/>
</declare-styleable>
</resources>
I am sharing my own custom class as an example. So for all custom function you need to first check if it is in EditMode or not. isInEditMode() tells us is the layout is being render by IDE preview or by phone. IDE preview is not so much powerful to run our custom codes, so it is recommended to do not run our custom while rendering by preview. I am attaching my custom class to just understand the exact scenario.
public class DynamicImageView extends ImageView {
static DynamicImageTypeListener masterImageTypeListener;
DynamicImageTypeListener imageTypeListener;
int imageResourceArray;
public DynamicImageView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
initValues(attr, defStyle);
}
public DynamicImageView(Context context, AttributeSet attr) {
super(context, attr);
initValues(attr, 0);
}
public DynamicImageView(Context context) {
super(context);
}
public static void setMasterImageTypeListener(DynamicImageTypeListener masterImageTypeListener) {
DynamicImageView.masterImageTypeListener = masterImageTypeListener;
}
public void setImageTypeListener(DynamicImageTypeListener imageTypeListener) {
this.imageTypeListener = imageTypeListener;
}
private void initValues(AttributeSet attr, int defStyle) {
if (!isInEditMode()) { //This code will run only on phone not by preview.
TypedArray a = getContext().obtainStyledAttributes(attr, R.styleable.DynamicImageView, defStyle, 0);
imageResourceArray = a.getResourceId(0, 0);
updateImage();
a.recycle();
}
}
public void updateImage() {
TypedArray imgs = getResources().obtainTypedArray(imageResourceArray);
int imageIndex = imageTypeListener != null ? imageTypeListener.getImageType(getContext()) : (masterImageTypeListener != null ? masterImageTypeListener.getImageType(getContext()) : 0);
setImageResource(imgs.getResourceId(imageIndex, -1));
}
}
Hope it will help :)
I have a Fragment which includes a custom/compound view in layout.
<app.view.ControlLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:id="#+id/controlLayout"
android:layout_width="match_parent"
android:layout_height="#dimen/fragment_control_size"
android:layout_marginBottom="#dimen/offset_distance"
custom:horizontal="true" />
Fragment has been attached to different activities in layouts, such as:
<fragment
android:id="#+id/controlLayout"
android:name="app.controllers.ControlFragment"
android:layout_width="match_parent"
android:layout_height="#dimen/fragment_control_size"
android:layout_alignParentBottom="true"
android:layout_marginBottom="#dimen/offset_distance"
tools:layout="#layout/fragment_control" />
The custom view has following attributes:
<resources>
<declare-styleable name="ControlLayout">
<attr name="horizontal" format="boolean" />
<attr name="callBtnVisibility" format="boolean" />
</declare-styleable>
</resources>
Based on to which Activity my Control Fragment is attached, I want to pass callBtnVisibility attribute to my custom view. One solution is handle the login in the code without using attributes:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_control, container, false);
if (getActivity() instanceof MyActivity) {
((ControlLayout) view.findViewById(R.id.controlLayout)).hideCallButton();
}
I wonder if I can somehow manage it without using following login. So that before controlLayout get inflated in the fragment, I can pass the attribute to it, and handle the logic in Constructor or init method of custom view:
public ControlLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
final TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.ControlLayout,
0, defStyle);
final boolean horizontal = a.getBoolean(R.styleable.ControlLayout_horizontal, false);
final boolean callButtonIsVisible = a.getBoolean(
R.styleable.ControlLayout_callBtnVisibility, true);
a.recycle();
if(callButtonIsVisible) {
mCallButton = new Button(getContext());
mCallButton.setId(R.id.button_call);
...
onInflated in fragment lifecycle which is called before onAttach() can solve this issue.
I created a custom layout containing a TextView and a Spinner that extends LinearLayout(called LabeledSpinner).
I put several of these LabeledSpinners into my main RelativeLayout and obviously I want to set different text for each of their textviews but I don't know how to do it in the xml file.
I need to access the attributes of the inner Views somehow please help.
I tried to use [package name].LabeledSpinner.label:text but it didn't work
(label is the id of the textview)
Make LabeledSpinner a styleable (res/values/attrs.xml)
<declare-styleable name="LabeledSpinner">
<attr name="text" format="string"/>
</declare-styleable>
In your LabeledSpinner
public LabeledSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LabeledSpinner);
String text = a.getString(R.styleable.LabeledSpinner_text);
a.recycle();
}
Note that the text attribute in LabeledSpinner is to be used as LabeledSpinner_text.
Now in your layouts
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:yourpackage="http://schemas.android.com/apk/res/com.package">
<com.package.LabeledSpinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
yourpackage:text="This is where your text goes"/>
</LinearLayout>
That should do!