I am making a custom layout, but it's not showing, and i do not know why.
Here is the XML file where the class is defined
<com.example.name.gw2applicaton.SpecializationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="65dp"
android:layout_height="match_parent">
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
</LinearLayout>
</com.example.name.gw2applicaton.SpecializationView>
Here is the class, just a constructor
public class SpecializationView extends LinearLayout {
public SpecializationView(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.layout_specialization, this, true);
}
}
And finally where the class is used
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.example.name.gw2applicaton.SpecializationView
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical">
</com.example.name.gw2applicaton.SpecializationView>
</LinearLayout>
</LinearLayout>
The SpecializationView is not visible, I do not know why.
What am I doing wrong here?
That's not how it works for a custom view, as you are trying to do. Use this convention instead:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- just include the layout you defined else where -->
<include layout="#layout/layout_specialization"/>
</LinearLayout>
Where layout_specialization.xml is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="65dp"
android:layout_height="match_parent">
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
</LinearLayout>
Note: You should use custom view definitions when you need to modify an existing view or viewgroup to have special programatic functionality, such as positioning, dynamic content, niche widget, etc... When you want to use a view like you are where it is just using existing widget functionality, do as I described. The include xml tag is great for defining an xml layout and re-using it through your project so there is a minimized duplication of code.
EDIT:
The reason you layout is not showing by the way is you have only defined the constructor for programmatically creating a view (via java code, not xml). To allow for an xml definition of your custom view extend the class as follows with the additional constructors needd:
public class SpecializationView extends LinearLayout {
/* Programmatic Constructor */
public SpecializationView(Context context) {
super(context);
init(context, null, 0);
}
/* An XML Constructor */
public SpecializationView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
/* An XML Constructor */
public SpecializationView(Context context, AttributeSet attrs, int resId) {
super(context, attrs, resId);
init(context, attrs, resId);
}
/**
* All initialization happens here!
*/
private void init(Context context, AttributeSet attrs, int resId){
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.layout_specialization, this, true);
}
}
This definition now includes the xml ability to create the custom view (which should now probably work for you). The reason it will work is now you send the attribute set, or the attributes definied via xml to the constructor. Since you didn't include it, it doesn't know what to do for your custom view when defined in xml and you cannot access the layout's attributes that you may define as custom.
Related
I'm trying to figure out a good solution on how to extend EditText to allow me to have other Views layered on top. I am trying to make a custom View that is an EditText that has a TextView on top to display the number of characters, and an ImageView on top for a clear button.
Currently, I have it working with extending FrameLayout, but that doesn't give me the control/flexibility that I am looking for. Such as I can't use ButterKnife's #OnTextChanged as it expects a TextView, and I don't have direct access to any XML attributes of the EditText unless I pass them through.
Thanks for your time.
ClearableEditText.java
public class ClearableEditText extends FrameLayout {
public ClearableEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
protected void init() {
View inflate = inflate(getContext(), R.layout.clearable_edit_text, this);
ButterKnife.bind(this, inflate);
...
}
...
}
R.layout.clearable_edit_text.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="#+id/clearable_edit"
style="#style/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
<ImageView
android:id="#+id/clearable_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:src="#drawable/button_clear"
android:visibility="invisible"
tools:visibility="visible" />
<TextView
android:id="#+id/clearable_count"
style="#style/edit_text_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
tools:text="1200" />
</FrameLayout>
I find it simpler in these situations to use an empty layout in the xml which I later fill on runtime with whatever elements I want. example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView ..... />
<Button........./>
.
.
.
<LinearLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
and in the activity
LayoutInflater inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout container = (LinearLayout)findViewById(R.id.container);
MyComplexView myComplexView = new MyComplexView(inflater, container);
where MyComplexView:
public static class MyComplexView{
LinearLayout container;
LayoutInflater inflater;
TextView textView ;
ImageView img;
EditText editText;
public MyComplexView(LinearLayout container,LayoutInflater inflater ){
this.container = container;
this.inflater = inflater;
View v = (View) inflater.inflate(R.layout.mylayout, null);
container.addView(v);
textView = (TextView)v.findViewById(R.id.textview);
img = (ImageView)v.findViewById(R.id.imgaviev);
editText = (EditText)v.findViewById(R.id.edittext);
// assign whatever text change listeners or on click listeners you want
}
public void makeEditTextMultiline(boolean flag){
if(flag){
edittext.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_FLAG_MULTI_LINE);
}
else{
edittext.setSingleLine(true);
}
}
public String getEditTextText(){
return editText.getText().toString();
}
}
After that you can create all sorts of methods in the MyComplexView class to manipulate the object.
I think it's easier this way than extending the View class.
I'm trying to implement a simple custom dial pad view. It was all working fine until I tried to use <include> to include a "template" for my buttons instead of explicitly defining each button in the XML file. Since I'm gonna have 12 near-identical buttons I thought I'd just make my XML a bit neater and shorter by doing this.
The problem is that in onFinishInflate() in my custom view, when I call findViewById(R.id.dialpad_view_btn1) it returns null. I am of course assigning the id dialpad_view_btn1 to one of my <include>'s. If I simply define the button explicitly instead of including from my "template" it works fine. Is there any workaround for this, or should I just accept that findViewById() and <include> don't play well together?
DialPadView.java
public class DialPadView extends android.support.v7.widget.GridLayout {
public DialPadView(Context context) {
super(context);
initializeViews(context);
}
public DialPadView(Context context, AttributeSet attrs) {
super(context, attrs);
initializeViews(context);
}
public DialPadView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context);
}
private void initializeViews(Context context) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.dialpad_view, this);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// This call to findViewById() results in a NullPointerException
((ImageButton) findViewById(R.id.dialpad_view_btn1)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// I'm just a dummy
}
});
}
}
dialpad_view.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.me.myproject.DialPadView"
xmlns:grid="http://schemas.android.com/apk/res-auto"
android:id="#+id/dialpad_grid_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
grid:alignmentMode="alignMargins"
grid:columnCount="3"
grid:rowCount="4">
<include
android:id="#+id/dialpad_view_btn1"
layout="#layout/dialpad_button" />
<include
android:id="#+id/dialpad_view_btn2"
layout="#layout/dialpad_button" />
</android.support.v7.widget.GridLayout>
dialpad_button.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto">
<Button
android:layout_width="0dp"
android:layout_height="0dp"
grid:layout_columnWeight="1"
grid:layout_rowWeight="1"
grid:layout_gravity="fill"
android:layout_margin="5dp"
android:scaleType="centerInside"
android:text="Test" />
</merge>
Problem with ids:
The problem you encountered is caused by mixing <include> and <merge> tags.
Please notice that <merge> serves different purpose and it should not be used in this case, so please use only <incluce> and make <Button> a root in your dialpad_button.xml file. Then all ids should be assigned properly.
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto">
android:layout_width="0dp"
android:layout_height="0dp"
grid:layout_columnWeight="1"
grid:layout_rowWeight="1"
grid:layout_gravity="fill"
android:layout_margin="5dp"
android:scaleType="centerInside"
android:text="Test" />
Details:
If you are curious about this problem you can check the parseInclude() method in LayoutInflater source file. You can find there a code handling <merge> inflation. You can see that the code for passing layout and id attributes to the child of <include> is not invoked for <merge> because <merge> should be used for different scenarios. there:
if (TAG_MERGE.equals(childName)) {
// Inflate all children.
rInflate(childParser, parent, childAttrs, false);
} else {
[...]
// We try to load the layout params set in the <include /> tag. If
// they don't exist, we will rely on the layout params set in the
// included XML file.
[...]
// Attempt to override the included layout's android:id with the
// one set on the <include /> tag itself.
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.View, 0, 0);
int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
// While we're at it, let's try to override android:visibility.
[...]
}
Other problems:
It's not strictly related with your problem with missing ids, but as pskink mentioned you wrongly inflate <android.support.v7.widget.GridLayout> inside your custom DialPadView so you will end up with hierarchy like this:
<DialPadView>
<android.support.v7.widget.GridLayout>
<Button/>
<Button/>
</android.support.v7.widget.GridLayout>
</DialPadView>
but <DialPadView> is already an instance of android.support.v7.widget.GridLayout, so basically you end up having GridLayout inside of GridLayout. Apart from not being optimal it may result in layout looking in a different way than you've imagined it to look and behave.
Using Android Studio, I get this error in my activity layout : Error:(9) No resource identifier found for attribute 'headerView' in package 'com.merahjambutech.zuki.deteksi'
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
xmlns:compat="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.merahjambutech.zuki.deteksi.view.PullToZoomListViewEx
android:id="#+id/paralax_social_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#android:color/transparent"
app:headerView="#layout/header_parallax_social" />
</LinearLayout>
I'm very sure the layout header_parallax_social.xml is available in my project files (res/layout), here's the code of header_parallax_social:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="160dp" >
<ImageView
android:id="#+id/header_parallax_social_new_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_gravity="center_horizontal"
android:contentDescription="#string/cd_main_image"
android:scaleType="centerCrop"
android:src="#drawable/parallax_social_small" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:orientation="vertical" >
</LinearLayout>
</RelativeLayout>
I have tried to change xmlns:app and anything like that, but still not found solution...
You have to set custom attribute i.e. headerView for your Listview in attrs.xml in values folder :
attrs.xml
<resources>
<declare-styleable name="PullToZoomListViewEx"> declare your custom listview class name here
<attr name="headerView" format="reference"/>
</declare-styleable>
</resources>
By doing this i hope app:headerView="#layout/header_parallax_social" will not show any error but to show header view in a listview you have to do some changes in your custom Listview class and it should looks like
public class PullToZoomListViewEx extends ListView{
private int headerId;
public PullToZoomListViewEx(Context context) {
super(context);
}
public PullToZoomListViewEx(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PullToZoomListViewEx(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PullToZoomListViewEx, defStyle, defStyle);
try {
headerId = a.getResourceId(R.styleable.PullToZoomListViewEx_headerView, View.NO_ID);
if (headerId != View.NO_ID) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View header = inflater.inflate(headerId, null);
addHeaderView(header);
}
} finally {
a.recycle();
}
}
}
or
If you want to avoid above efforts, You can programmatically set a header view to a Listview like this :
LayoutInflater inflater = getLayoutInflater();
ViewGroup header = (ViewGroup)inflater.inflate(R.layout.header,myListView, false);
//replace R.layout.header with R.layout.header_parallax_social and myListView with your listview object
myListView.addHeaderView(header, null, false);
Hope this helps.
Im trying to get my menu accessible across my app with out having to add the XML code to all my activities as well as the code in all the classes.
I already have a "BaseController" class that handle some aspects of the menu like the TabHost and text etc but I am unable to get the menu extrapolated so it can be used across the app without having to retype all my code over and over again?
HomeActivity.class
public class HomeActivity extends BaseController {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_home);
Log.i(TAG, "onCreate has been called");
menu();
mDrawerLayout =(CustomDrawerLayout)findViewById(R.id.home_activity);
left = (RelativeLayout) findViewById(R.id.left_menu);
menu_button = (ImageView) findViewById(R.id.menuImageButton)
}
private void openMenu() {
if (hasLoggedInBefore() == false) {
goToLogin();
} else {
mDrawerLayout.openDrawer(left);
}
}
activity_home.xml
<com.myapp.app.ui.CustomDrawerLayout
android:id="#+id/home_activity"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/bg"
tools:context=".MainActivity">
<include
layout="#layout/home_screen_items"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<RelativeLayout
android:id="#+id/left_menu"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/bob_orange6">
<TabHost
android:id="#+id/menuTabs"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TabWidget
android:id="#android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</TabWidget>
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/menu_tab1_tab"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab1 Tab Text"/>
</LinearLayout>
<LinearLayout
android:id="#+id/menu_tab2_tab"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab2 Tab Text"/>
</LinearLayout>
<LinearLayout
android:id="#+id/menu_tab3_tab"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab3 Tab Text"/>
</LinearLayout>
</FrameLayout>
</LinearLayout>
</TabHost>
</RelativeLayout>
as you can see above my menu is in the HomeActivity XML and if i try add it using the tag, the menu overlays the content and does not collapse.
The only way it works is if i do it the way i have above.
my BaseController.java
public class BaseController extends Activity {
CustomDrawerLayout mDrawerLayout;
RelativeLayout left;
public void menu(){
final TabHost menuTab = (TabHost)findViewById(R.id.menuTabs);
menuTab.setup();
TabHost.TabSpec tab1_tab_menu = menuTab.newTabSpec("Tab1");
tab1_tab_menu.setContent(R.id.menu_tab1_tab);
tab1_tab_menu.setIndicator("Tab1");
menuTab.addTab(tab1_tab_menu);
TabHost.TabSpec tab2_tab_menu = menuTab.newTabSpec("Tab2");
tab2_tab_menu.setContent(R.id.menu_tab2_tab);
tab2_tab_menu.setIndicator("Tab2");
menuTab.addTab(tab2_tab_menu);
TabHost.TabSpec tab3_tab_menu = menuTab.newTabSpec("Tab3");
tab3_tab_menu.setContent(R.id.menu_tab3_tab);
tab3_tab_menu.setIndicator("Tab3");
menuTab.addTab(tab3_tab_menu);
}
}
incase its needed i have include the CustomDrawerLayout code
CustomDrawerLayout.java
public class CustomDrawerLayout extends DrawerLayout{
public CustomDrawerLayout(Context context) {
super(context);
}
public CustomDrawerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
The idea is to have it in the baseController class and pretty much call a method in the onCreate in a specific activity that i want the menu in as well as say in the xml for that Activity and the menu is basically there with the same content etc?
Been battling with this for a couple days now. Help will be greatly appreciated.
Thanks
Implemention for UrMenu should be done in BaseControllerActivity, and all other activities in our application must inhert this BaseControllerActivity. Then you can use that menu, and you can override if want to customize onClick of menu's.
I want to place the two text horizontally at the center. It is very easy to put it directly in a layout.
But what i am trying to achieve is :
Testapplayout.xml: This is set as content view of the activity.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<com.example.testapp.Customlayout
android:id="#+id/custom"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="2dp"
android:layout_marginTop="2dp">
</com.example.testapp.Customlayout>
</LinearLayout>
class Customlayout
public class Customlayout extends LinearLayout {
public Customlayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public Customlayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Customlayout(Context context) {
this(context, null, 0);
}
}
Now on the Testapplayout, customlayout, I am trying to inflate the layout to show the texts at the center:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center|bottom"
android:orientation="horizontal" >
<TextView
android:id="#+id/status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="text1"
android:layout_gravity="center"
/>
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/status_text"
/>
</LinearLayout>
This still comes at the
try
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relative_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.example.testapp.Customlayout
android:id="#+id/custom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
</com.example.testapp.Customlayout>
</RelativeLayout>