当前位置: 首页 > 知识库问答 >
问题:

为android创建自定义拨号板视图

李跃
2023-03-14

main活动中创建视图。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_视图中指定了布局组件。xmlas

<?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
    }
}

我的main活动。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);
    }
}

我猜我还没有实现我的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);
    }
}

已解决当时与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个答案

干京
2023-03-14

这就是我解决问题的方法。它应该适用于门廊和景观。

我在此呈现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) {}
 类似资料:
  • 本文向大家介绍Android 创建自定义视图,包括了Android 创建自定义视图的使用技巧和注意事项,需要的朋友参考一下 示例 如果需要完全自定义的视图,则需要子类View(所有Android视图的超类),并提供自定义的sizing(onMeasure(...))和drawing(onDraw(...))方法: 创建您的自定义视图框架:每个自定义视图的基本相同。在这里,我们为自定义视图创建框架,

  • 前言:我从一个带有小键盘的旧翻盖手机搬到了S3,用它我可以很容易地键入电话银行密码或类似的东西,在我键入时用另一只手屏蔽了一只手。 如果我将手机旋转到横向模式,我最初会得到一个尺寸较小的拨号器,但一旦我输入电话(比如语音邮件),它会跳回到纵向模式和股票拨号器。从我对自定义拨号器所做的研究来看,一旦呼叫开始,它们似乎就不起作用了,所以我不太抱希望。 唯一的解决办法是根植手机并把它黑得很深吗?(显然,

  • 模拟ios5的拨号键盘,带有拨号键,可以调用系统的电话功能进行打电话。只有在真机中才能测试打电话功能。 作者说:修改delegate后,可以自定义电话。 [Code4App.com]

  • 我已经使用CPT插件创建了一个自定义的post类型产品,然后我创建了三个自定义分类法,用于添加新产品,比如品牌、用法和类型。 我试图实现的是有一个页面,其中列出了自定义分类法中的所有术语,单击该分类法,您将进入一个单独的术语页面,该页面列出了使用该术语标记的所有产品。最后,单击一个产品会将您带到单个产品页面。 基本上我想要这个:家- 这对我来说都是全新的和未知的。我是用分类法发现的。php为我的分

  • 有人能帮我创建balow图像剪切搜索栏吗?我已经用自定义拇指和分段文本浏览过SeekBar,还有SeekBar拇指位置问题 但是我没有成功创建我的客户搜索栏,请帮助我

  • 本文向大家介绍Android如何创建自定义ActionBar,包括了Android如何创建自定义ActionBar的使用技巧和注意事项,需要的朋友参考一下 当多个界面都有很多相似部分时,可以考虑创建一个功能较全的模板。而在需要时,可以通过引用模板来实现自己想要实现的功能。比如适配器 Adapter,当很多的适配器都差不多时,就可以通过打造一个通用的适配器来实现。本例中主要是如何创建自定义的 Act