5

I found this example of Custom LinearLayout on stackoverflow but it throws errors when i try to run it, can someone find what's wrong with it?

Custom LinearLayout:

package com.example.androidapp.widgets;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;

public class ExpandablePanel extends LinearLayout {

    private final int mHandleId;
    private final int mContentId;

    private View mHandle;
    private View mContent;

    private boolean mExpanded = true;
    private int mCollapsedHeight = 0;
    private int mContentHeight = 0;

    public ExpandablePanel(Context context) {
        this(context, null);
    }

    public ExpandablePanel(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ExpandablePanel, 0, 0);

        // How high the content should be in "collapsed" state
        mCollapsedHeight = (int) a.getDimension(
            R.styleable.ExpandablePanel_collapsedHeight, 0.0f);

        int handleId = a.getResourceId(R.styleable.ExpandablePanel_handle, 0);
        if (handleId == 0) {
            throw new IllegalArgumentException(
                "The handle attribute is required and must refer "
                    + "to a valid child.");
        }

        int contentId = a.getResourceId(R.styleable.ExpandablePanel_content, 0);
        if (contentId == 0) {
            throw new IllegalArgumentException(
                "The content attribute is required and must refer "
                    + "to a valid child.");
        }

        mHandleId = handleId;
        mContentId = contentId;

        a.recycle();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        mHandle = findViewById(mHandleId);
        if (mHandle == null) {
            throw new IllegalArgumentException(
                "The handle attribute is must refer to an"
                    + " existing child.");
        }

        mContent = findViewById(mContentId);
        if (mContent == null) {
            throw new IllegalArgumentException(
                "The content attribute is must refer to an"
                    + " existing child.");
        }

        mHandle.setOnClickListener(new PanelToggler());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mContentHeight == 0) {
            // First, measure how high content wants to be
            mContent.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
            mContentHeight = mContent.getMeasuredHeight();
        }

        // Then let the usual thing happen
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private class PanelToggler implements OnClickListener {
        @Override
        public void onClick(View v) {
            Animation a;
            if (mExpanded) {
                a = new ExpandAnimation(mContentHeight, mCollapsedHeight);
            } else {
                a = new ExpandAnimation(mCollapsedHeight, mContentHeight);
            }
            a.setDuration(500);
            mContent.startAnimation(a);
            mExpanded = !mExpanded;
        }
    }

    private class ExpandAnimation extends Animation {
        private final int mStartHeight;
        private final int mDeltaHeight;

        public ExpandAnimation(int startHeight, int endHeight) {
            mStartHeight = startHeight;
            mDeltaHeight = endHeight - startHeight;
        }

        @Override
        protected void applyTransformation(float interpolatedTime,
            Transformation t) {
            android.view.ViewGroup.LayoutParams lp = mContent.getLayoutParams();
            lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime);
            mContent.setLayoutParams(lp);
        }

        @Override
        public boolean willChangeBounds() {
            // TODO Auto-generated method stub
            return true;
        }
    }
}

res > values > attrs.xml

<?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" />
  </declare-styleable>
</resources>

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cl="http://schemas.android.com/apk/lib/com.example.androidapp.widgets"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

<com.example.androidapp.widgets.ExpandablePanel
    android:orientation="vertical"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    cl:handle="@+id/expand"
    cl:content="@+id/value"
    cl:collapsedHeight="50dip">
    <TextView
        android:id="@id/value"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:maxHeight="50dip"
        />
    <Button
        android:id="@id/expand"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="More" />
</com.example.androidapp.widgets.ExpandablePanel>


</LinearLayout>

Activity class:

package com.example.androidapp.widgets;

import android.app.Activity;
import android.os.Bundle;

public class GoodPanelsActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

LogCat error:

FATAL EXCEPTION: main java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.androidapp.widgets/com.example.androidapp.widgets.GoodPanelsActivity}: android.view.InflateException: Binary XML file line #9: Error inflating class com.example.androidapp.widgets.ExpandablePanel

EDIT: more from LogCat

java.lang.reflect.InvocationTargetException at java.lang.reflect.Constructor.constructNative(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:415) at android.view.LayoutInflater.createView(LayoutInflater.java:505) Caused by: java.lang.IllegalArgumentException: The handle attribute is required and must refer to a valid child. at com.example.androidapp.widgets.ExpandablePanel.(ExpandablePanel.java:39)

Community
  • 1
  • 1
Yaqub Ahmad
  • 27,569
  • 23
  • 102
  • 149

3 Answers3

6

At last i found the issue:

look at below line in main.xml:

xmlns:cl="http://schemas.android.com/apk/lib/com.example.androidapp.widgets"

it should be:

xmlns:cl="http://schemas.android.com/apk/res/com.example.androidapp.widgets"

Because i am not using external libraries & the ExpandablePanel exists under the res.

Cheers.

Yaqub Ahmad
  • 27,569
  • 23
  • 102
  • 149
0

You need to add the project that contains this class com.example.androidapp.widgets.ExpandablePanel to your project buildpath. If this is not an open source project, or the source is not available you are not able to do this.

If you do have that project, and it is a LibraryProject you should right click on your Project->Properties->Android and in the Library section add that project.

If is a normal Project just go to Properties->Java Build Path->Projects and from there add that project.

Ovidiu Latcu
  • 71,607
  • 15
  • 76
  • 84
-1

I was also trying out this script, as I'm new to Android programming and I was looking for some good examples to learn from.

My app force closes when I add the layout to the main.xml.

When I add the layout for the ExpendablePanel, the graphical layout becomes grayed out and I can't edit it:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cl="http://schemas.android.com/apk/lib/com.martin.myapp.widgets"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

<com.martin.myapp.widgets.ExpandablePanel
    android:orientation="vertical"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    cl:handle="@+id/expand"
    cl:content="@+id/value"
    cl:collapsedHeight="50dip">
    <TextView
        android:id="@id/value"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:maxHeight="50dip"
        />
    <Button
        android:id="@id/expand"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="More" />
</com.martin.myapp.widgets.ExpandablePanel>

So this is essentially an empty app without the custom widget.

I made a ExpandablePanel.java and copied the script that extends LinearLayout in there, gives me no errors. My attrs.xml is also error free.

Eclipse gives me no errors when running. It runs and sends it to my phone fine. That's where it force closes. When I remove the ExpandablePanel layout in main.xml it runs and is an empty app.

I really can't figure it out.