为android创建自定义Dialpad视图

时间:2018-05-01 07:33:22

标签: android android-layout

我一直在努力设计像这样的拨号盘视图 portrate view
在portrate和
landscape view
lanscape view

要创建视图,在MainActivity.java中,我有

DialPadView myDialPad = new DialPadView(context);
setContentView(myDialPad);

所有图片都已提供,当用户点击按钮时,它必须以某种方式改变。

我在res/drawable/dialpad0.xml

 <?xml version="1.0" encoding="utf-8"?>
     <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_dialpad_0_pink" android:state_pressed="true" android:state_selected="true"/>
    <item android:drawable="@drawable/ic_dialpad_0_blue_dark" android:state_focused="true" />
    <item android:drawable="@drawable/ic_dialpad_0_blue" />
    </selector>

我已将res/layout/dialpad_view.xml中的布局组件指定为

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

<ImageButton
    android:id="@+id/dialpad_1"
    style="@style/dialpadStyle"
    android:contentDescription="@string/dialpad_1"
    android:background="@drawable/dialpad1" />

<ImageButton
    android:id="@+id/dialpad_2"
    style="@style/dialpadStyle"
    android:contentDescription="@string/dialpad_2"
    android:background="@drawable/dialpad2" />

<ImageButton
    android:id="@+id/dialpad_3"
    style="@style/dialpadStyle"
    android:contentDescription="@string/dialpad_3"
    android:background="@drawable/dialpad3" />
...
// others
...

<ImageButton
    android:id="@+id/dialpad_star"
    style="@style/dialpadStyle"
    android:contentDescription="@string/dialpad_star"
    android:background="@drawable/dialpadstar" />

<ImageButton
    android:id="@+id/dialpad_0"
    style="@style/dialpadStyle"
    android:contentDescription="@string/dialpad_0"
    android:background="@drawable/dialpad0" />

<ImageButton
    android:id="@+id/dialpad_pound"
    style="@style/dialpadStyle"
    android:contentDescription="@string/dialpad_pound"
    android:background="@drawable/dialpadpound" />
</merge>

其中@string/dialpadStyle指定为(res/values/styles.xml}内部

<style name="dialpadStyle" parent="Widget.AppCompat.ImageButton">
        <item name="android:focusableInTouchMode">true</item>
        <item name="android:focusable">true</item>
        <item name="android:visible">true</item>
        <item name="android:height">@dimen/dialpadHeight</item> // 150dp
        <item name="android:width">@dimen/dialpadWidth</item> // 150dp 
    </style>

这就是DialPadView.java的样子

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.ImageButton;
import android.widget.TableLayout;

import java.util.ArrayList;
import java.util.List;

import mypackage.R;

public class DialPadView extends TableLayout {
    private ImageButton dialpad;

    public DialPadView(final Context context){
        super(context);
        init(context, null);
    }
    public DialPadView(final Context context, @Nullable final AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(final Context context, @Nullable final AttributeSet attributeSet) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        try {
            inflater.inflate(R.layout.dialpad_view, this, true);
        } catch (Exception e) {
            Log.e("LayoutInflationError", e.getMessage());
        }
        List<ImageButton> buttons = new ArrayList<>();
        int index = 0;
        while (getChildAt(index) != null) {
            ImageButton button = (ImageButton) getChildAt(index);
            buttons.add(button);
            index++;
        }
        dialpad = (ImageButton) buttons.get(11); // just for experimenting
        Log.i("Buttons", "" + buttons.size()); // gives Buttons 12
    }
}

我的MainActivity.java看起来像这样

package myPackage;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import myPackage.customView.DialPadView; 

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DialPadView dialpad = new DialPadView(this);
        setContentView(dialpad);
    }
}

这给了我 myDialPad

我猜我没有很好地实现我的DialPadView.java课程或问题在哪里。任何提示/帮助将不胜感激。

在评论之后我将init功能修改为这样

private void init(final Context context, @Nullable final AttributeSet attributeSet) {
        setStretchAllColumns(true);
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        try {
            inflater.inflate(R.layout.dialpad_view, this, true);
        } catch (Exception e) {
            Log.e("LayoutInflationError", e.getMessage());
        }
        List<ImageButton> buttons = new ArrayList<>();
        int index = 0;
        while (getChildAt(index) != null) {
            ImageButton button = (ImageButton) getChildAt(index);
            buttons.add(button);
            index++;
        }
        Log.i("Buttons|=>", "" + buttons.size()); // gives Buttons|=>: 12
        // attempting to add rows
        for (int i = 0; i < buttons.size(); i++) {
            TableRow row = new TableRow(context);
            TableRow.LayoutParams lp = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT);
            row.setLayoutParams(lp);
            dialpad = (ImageButton) buttons.get(i); // get an image button
            row.addView(dialpad, i % 3); // add ImageButton to this row
            if ((i + 1) % 3 == 0) {
                addView(row, i);// add the row to the TableLayout after every 3rd entry
            }
        }
    }

在这种情况下,程序崩溃并出现以下错误

05-01 16:36:06.404 3269-3269 myPackage E/AndroidRuntime: FATAL EXCEPTION: main
    Process: myPackage, PID: 3269
    java.lang.RuntimeException: Unable to start activity ComponentInfo{myPackage/myPackage}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
     Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
        at android.view.ViewGroup.addViewInner(ViewGroup.java:4915)
        at android.view.ViewGroup.addView(ViewGroup.java:4746)
        at android.view.ViewGroup.addView(ViewGroup.java:4686)
        at myPackage.customView.DialPadView.init(DialPadView.java:52)
        at myPackage.customView.DialPadView.(DialPadView.java:22)
        at myPackage.MainActivity.onCreate(MainActivity.java:17)
        at android.app.Activity.performCreate(Activity.java:6975)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6541) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 

已修改为

DialPadView.java

public class DialPadView extends TableLayout {
    private List<Button> buttons;

    public DialPadView(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        buttons = new ArrayList<>();
        loadButtonImages();
        removeAllViews();
        loadRowsForPortrate(context);
    }

    /**
     * Give button all desired default functionality
     *
     * @param button Button to style
     */
    private void styleButton(final Button button) {
        button.setMaxHeight((int) getResources().getDimension(R.dimen.dialpadHeight));
        button.setMaxWidth((int) getResources().getDimension(R.dimen.dialpadHeight));
        button.setFocusable(true);
        button.setClickable(true);
        button.setFocusableInTouchMode(true);
        button.setScaleX(0.12f);
        button.setScaleY(0.12f);
    }

    /**
     * Load the images to the various ImageButtons
     */
    private void loadButtonImages() {
        for (int i = 0; i < 12; i++) {
            Button button = new Button(getContext());
            styleButton(button);
            buttons.add(button);
        }

        // give each button, its background image
        if (!buttons.isEmpty()) {
            buttons.get(0).setBackgroundResource(R.drawable.dialpad1);
            buttons.get(1).setBackgroundResource(R.drawable.dialpad2);
            buttons.get(2).setBackgroundResource(R.drawable.dialpad3);
            buttons.get(3).setBackgroundResource(R.drawable.dialpad4);
            buttons.get(4).setBackgroundResource(R.drawable.dialpad5);
            buttons.get(5).setBackgroundResource(R.drawable.dialpad6);
            buttons.get(6).setBackgroundResource(R.drawable.dialpad7);
            buttons.get(7).setBackgroundResource(R.drawable.dialpad8);
            buttons.get(8).setBackgroundResource(R.drawable.dialpad9);
            buttons.get(9).setBackgroundResource(R.drawable.dialpadstar);
            buttons.get(10).setBackgroundResource(R.drawable.dialpad0);
            buttons.get(11).setBackgroundResource(R.drawable.dialpadpound);
        }
    }

    // load rows for portrate view
    private void loadRowsForPortrate(Context context) {
        // Define row parameters
        TableRow.LayoutParams params = new TableRow.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT);

        params.setMargins(2, 2, 2, 2);

        List<TableRow> tableRows = new ArrayList<>();

        // create 4 rows and give them the row parameters above
        for (int rows = 0; rows < 4; rows++) {
            TableRow row = new TableRow(context);
            row.setLayoutParams(params);
            row.setGravity(Gravity.CENTER_VERTICAL);
            tableRows.add(row);
        }

        // first row stuff
        TableRow row1 = (TableRow) tableRows.get(0);
        row1.addView(buttons.get(0), 0);
        row1.addView(buttons.get(1), 1);
        row1.addView(buttons.get(2), 2);

        // Create second row and fill it with three imageButtons
        TableRow row2 = (TableRow) tableRows.get(1);
        row2.addView(buttons.get(3), 0);
        row2.addView(buttons.get(4), 1);
        row2.addView(buttons.get(5), 2);

        // third row
        TableRow row3 = (TableRow) tableRows.get(2);
        row3.addView(buttons.get(6), 0);
        row3.addView(buttons.get(7), 1);
        row3.addView(buttons.get(8), 2);

        // Fourth row
        TableRow row4 = (TableRow) tableRows.get(3);
        row3.addView(buttons.get(9), 0);
        row3.addView(buttons.get(10), 1);
        row3.addView(buttons.get(11), 2);

        // add all rows to table
        this.addView(row1);
        this.addView(row2);
        this.addView(row3);
        this.addView(row4);

    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DialPadView dialPadView = new DialPadView(this);
        setContentView(dialPadView);
    }
}

但我只在屏幕上显示一个图像(编号1)
enter image description here

解决的 然后与TableRows合并

<TableRow>

    <ImageButton
        android:id="@+id/oneButton"
        style="@style/dialpadStyle"
        android:background="@drawable/dialpad1"
        android:contentDescription="@string/dialpad_1" />

    <ImageButton
        android:id="@+id/twoButton"
        style="@style/dialpadStyle"
        android:background="@drawable/dialpad2"
        android:contentDescription="@string/dialpad_2" />

    <ImageButton
        android:id="@+id/threeButton"
        style="@style/dialpadStyle"
        android:background="@drawable/dialpad3"
        android:contentDescription="@string/dialpad_3" />
</TableRow>

<TableRow>
//.... others

// in `DialPadView.java`
private void init(final Context context) {
   LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    try {
        inflater.inflate(R.layout.dialpad_view, this, true);
    } catch (Exception e) {
        Log.e("InflatorError", e.getMessage());
    }

    int index = 0;
    while (getChildAt(index) != null) {
       TableRow row = (TableRow) getChildAt(index);

        index++;
    }
}

1 个答案:

答案 0 :(得分:0)

这就是我解决问题的方式。它本应同时适用于速率和景观。

我在此介绍portrate模式和相关代码段。横向模式仅是重新排列dialpad_portrate.xml

的问题
// dialpad_portrate.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

<TableRow
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:gravity="center">

    <EditText
        android:id="@+id/dialedNumbers"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.7"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:hint="@string/dialedNumberHint"
        android:inputType="text"
        android:textSize="20sp" />

    <ImageButton
        android:id="@+id/deleteButton"
        style="@style/dialpadStyle"
        android:layout_weight="0.2"
        android:contentDescription="@string/deleteNumbers"
        android:longClickable="true"
        android:src="@drawable/ic_action_ic_delete_pink" />

    <ImageButton
        android:id="@+id/callButton"
        style="@style/dialpadStyle"
        android:layout_weight="0.1"
        android:contentDescription="@string/callDialedNumbers"
        android:src="@drawable/ic_call_pink" />
</TableRow>

<TableRow
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:gravity="center">

    <ImageButton
        android:id="@+id/oneButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/one"
        android:src="@drawable/ic_dialpad_1_blue" />

    <ImageButton
        android:id="@+id/twoButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/two"
        android:src="@drawable/ic_dialpad_2_blue" />

    <ImageButton
        android:id="@+id/threeButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/three"
        android:src="@drawable/ic_dialpad_3_blue" />
</TableRow>

<TableRow
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:gravity="center">

    <ImageButton
        android:id="@+id/fourButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/four"
        android:src="@drawable/ic_dialpad_4_blue" />

    <ImageButton
        android:id="@+id/fiveButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/five"
        android:src="@drawable/ic_dialpad_5_blue" />

    <ImageButton
        android:id="@+id/sixButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/six"
        android:src="@drawable/ic_dialpad_6_blue" />
</TableRow>

<TableRow
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:gravity="center">

    <ImageButton
        android:id="@+id/sevenButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/seven"
        android:src="@drawable/ic_dialpad_7_blue" />

    <ImageButton
        android:id="@+id/eightButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/eight"
        android:src="@drawable/ic_dialpad_8_blue" />

    <ImageButton
        android:id="@+id/nineButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/nine"
        android:src="@drawable/ic_dialpad_9_blue" />
</TableRow>

<TableRow
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:gravity="center">

    <ImageButton
        android:id="@+id/starButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/star"
        android:src="@drawable/ic_dialpad_star_blue" />

    <ImageButton
        android:id="@+id/zeroButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/zero"
        android:src="@drawable/ic_dialpad_0_blue" />

    <ImageButton
        android:id="@+id/poundButton"
        style="@style/dialpadStyle"
        android:contentDescription="@string/pound"
        android:src="@drawable/ic_dialpad_pound_blue" />
</TableRow>

</merge>

//DialPadView.java

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.media.SoundPool;
import android.media.ToneGenerator;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TableLayout;
import android.widget.TableRow;

import java.util.ArrayList;
import java.util.List;

import mypackage.R;
import mypackage.Constants;

public class DialPadView extends TableLayout {
private static final int CALL_PERMISSION_CODE = 1000;
private final String TAG = getClass().getSimpleName().toUpperCase();
private LayoutInflater inflater;
private List<Integer> sounds;
private SoundPool soundPool;
private boolean soundsReady = false;
private List<ImageButton> buttons;
private boolean canReadSounds;
private EditText dialer;
String fileLocation;

public DialPadView(final Context context) {
    super(context);
    init(context, null);
}

public DialPadView(final Context context, @Nullable final AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs);
}

/**
 * Init function that initializes everything and changes the display based on the orientation of
 * the screen
 * @param context The view's context
 * @param attributeSet Attributesets (null in my case)
 */
private void init(final Context context, @Nullable final AttributeSet attributeSet) {
    PreferenceManager.setDefaultValues(context, R.xml.app_preferences, false);

    canReadSounds =
        Constants.getBooleanFromPreference(Constants.READ_DEVICE_STORAGE, getContext());

    fileLocation = getFileLocation(); // location of sound file

    setBackgroundColor(getResources().getColor(R.color.dialpadBackground));

    inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    buttons = new ArrayList<>();

 // check device orientation and present appropriate view
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
        makeLandscape();
    } else {
        makePortrate();
    }

    soundPool = new SoundPool(12, AudioManager.STREAM_MUSIC, 0);
    soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
        @Override
        public void onLoadComplete(final SoundPool soundPool, final int sampleId,
                                   final int status) {
            soundsReady = true;
        }
    });

    sounds = new ArrayList<>();
    if (canReadSounds) { // permission to read from user storage
        loadSounds();
    }

    doUserInteractions(); // when a user clicks on a number, a sound is played and the color changes. 
}


/**
 * Inflate rows for portrate display
 */
private void makePortrate() {
    try {
        inflater.inflate(R.layout.dialpad_portrate, this, true);
    } catch (Exception e) {
        Log.d("LayoutInflationError", e.getMessage());
    }

    int index = 0;
    while (getChildAt(index) != null) {
        if (index == 0) {
            TableRow actions = (TableRow) getChildAt(index);
            dialer = (EditText) actions.getChildAt(0);
        } else {
            TableRow row = (TableRow) getChildAt(index);
            for (int i = 0; i < row.getChildCount(); i++) {
                ImageButton v = (ImageButton) row.getChildAt(i);
                buttons.add(v);
            }
        }
        index++;
    }
}
// OTHER METHODS
 /**
 * Make landscape view
 */
private void makeLandscape() {}
/**
 * When an image is pressed, it plays a sound and its image changes from blue to pink
 */
@SuppressLint("ClickableViewAccessibility")
private void doUserInteractions() {}
 /**
 * Changes the ImageButton's background, plays the corresponding sound and clears on focused
 * buttons
 * @param button Current button on focuss
 */
private void performActions(ImageButton button) {}

/**
 * Change the image if it is on focus
 * @param view ImageButton
 */
private void changeImage(final ImageButton view) {}

/**
 * Restore image button if not focussed
 * @param view ImageButton
 */
private void restoreImageBackground(final ImageButton view) {}

 /**
 * If the current button is not on pressed on entered from the keyboard, restore the image
 * background
 * @param button current image (pressed or entered from the keyboard)
 */
private void restoreOtherImageResources(ImageButton button) {}

/**
 * Load the pre-defined sounds
 **/
private void loadSounds() {}

/**
 * Play a sound based on the image pressed
 * @param view ImageButton pressed
 */
private void playSound(ImageButton view) {}

/**
 * Play dmtf tones if user device does not have external storage
 * @param view the button pressed
 */
private void playDMTF(ImageButton view) {}

最终产品。 带圆圈的区域已添加到activity_main.xml中 参见enter image description here