使用Gallery创建向导表单

当需要用户填充一个较长的表单时,开发者或许会找不到头绪。在接下来的这个例子中,我们会使用Gallery控件创建一个具有多个表单项的用户注册表单。最终效果如下图所示。

这里写图片描述

要实现上述的向导表单,需要创建一个命名为CreateAccountActivity的Activity。我们为上述Activity使用Theme.Dialog样式。在该Activity中,我们会创建一个Gallery对象,并且用一个Adapter填充这个Gallery。因为该Adapter需要与Activity交互,因此我们使用Delegate委托接口。

先创建每个页面的公用视图,XML文件内容如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="250dp"
                android:layout_height="220dp"
                android:background="#AAAAAA">

    <!--在该LinearLayout中放置要显示的表单项-->
    <LinearLayout
        android:id="@+id/create_account_form"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:orientation="vertical"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp">

        <!--在LinearLayout第一个子视图中显示表单标题-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Account creation"
            android:textColor="#000000"
            android:textSize="20sp"
            android:textStyle="bold"/>
    </LinearLayout>

    <!--Next按钮用于切换到下一个页面-->
    <Button
        android:id="@+id/create_account_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:text="Next"
        android:textSize="12sp"/>

    <!--这个按钮只在最后一个页面显示,用于提交表单-->
    <Button
        android:id="@+id/create_account_create"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/create_account_form"
        android:gravity="center"
        android:paddingRight="45dp"
        android:text="Create Account"
        android:textSize="12sp"/>

</RelativeLayout>

既然有了公用视图的布局文件,我们就可以创建Adapter的代码。我们将该Adapter命名为CreateAccountAdapter,继承自BaseAdapter。代码如下:

package com.example.huangfei.hack2;

import android.content.Context;
import android.graphics.Color;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;

import com.example.huangfei.hack2.model.Account;
import com.example.huangfei.hack2.model.Countries;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * Created by huangfeihong on 2015/11/19.
 */
public class CreateAccountAdapter extends BaseAdapter {

    /**
     * 该接口用来与CreateAccountActivity进行交互
     */
    public static interface CreateAccountDelegate {
        int FORWARD = 1;
        int BACKWARD = -1;

        void scroll(int type);//该方法当点击”Next“按钮时执行

        void processForm(Account account);//当用户提交表单时,执行该方法
    }

    public static final String FULL_NAME_KEY = "fullname";
    public static final String EMAIL_KEY = "email";
    public static final String PASSWORD_KEY = "password";
    public static final String GENDER_KEY = "gender";
    public static final String CITY_KEY = "city";
    public static final String COUNTRY_KEY = "country";
    public static final String ZIP_KEY = "zipcode";

    private static final int FORMS_QTY = 4;
    private Context mContext;
    private LayoutInflater mInflator;
    private CreateAccountDelegate mDelegate;
    private HashMap<String, String> mFormData;
    private Account mAccount;

    public CreateAccountAdapter(Context ctx) {
        mContext = ctx;
        mInflator = LayoutInflater.from(ctx);
        mFormData = new HashMap<String, String>();
        mAccount = new Account();
    }

    @Override
    public int getCount() {
        return FORMS_QTY;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public void setDelegate(CreateAccountDelegate delegate) {
        mDelegate = delegate;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            //填充自定义View
            convertView = mInflator.inflate(
                    R.layout.create_account_generic_row, parent, false);
        }
        //获取放置表单项的LinearLayout
        LinearLayout formLayout = (LinearLayout) convertView
                .findViewById(R.id.create_account_form);

        //最后一页不显示”Next“按钮
        View nextButton = convertView
                .findViewById(R.id.create_account_next);
        if (position == FORMS_QTY - 1) {
            nextButton.setVisibility(View.GONE);
        } else {
            nextButton.setVisibility(View.VISIBLE);
        }
        if (mDelegate != null) {
            nextButton.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    mDelegate.scroll(CreateAccountDelegate.FORWARD);
                }
            });
        }

        //仅在最后一页显示提交表单按钮
        Button createButton = (Button) convertView
                .findViewById(R.id.create_account_create);
        if (position == FORMS_QTY - 1) {
            createButton.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    processForm();
                }
            });

            createButton.setVisibility(View.VISIBLE);
        } else {
            createButton.setVisibility(View.GONE);
        }

        //根据页面位置填充LinearLayout
        switch (position) {
            case 0:
                populateFirstForm(formLayout);
                break;

            case 1:
                populateSecondForm(formLayout);
                break;

            case 2:
                populateThirdForm(formLayout);
                break;

            case 3:
                populateFourthForm(formLayout);
                break;
        }

        return convertView;
    }

    /**
     * 创建第一个表单
     */
    private void populateFirstForm(LinearLayout formLayout) {
        formLayout.addView(createTitle(mContext
                .getString(R.string.account_create_full_name_title)));

        EditText nameEditText = createEditText(
                mContext.getString(R.string.account_create_full_name_hint),
                InputType.TYPE_CLASS_TEXT, EditorInfo.IME_ACTION_NEXT, false,
                FULL_NAME_KEY);

        if (mFormData.get(FULL_NAME_KEY) != null) {
            nameEditText.setText(mFormData.get(FULL_NAME_KEY));
        }

        formLayout.addView(nameEditText);
        formLayout.addView(createErrorView(FULL_NAME_KEY));

        formLayout.addView(createTitle(mContext
                .getString(R.string.account_create_email_title)));

        EditText emailEditText = createEditText(
                mContext.getString(R.string.account_create_email_hint),
                InputType.TYPE_CLASS_TEXT, EditorInfo.IME_ACTION_NEXT, true,
                EMAIL_KEY);

        if (mFormData.get(EMAIL_KEY) != null) {
            emailEditText.setText(mFormData.get(EMAIL_KEY));
        }

        formLayout.addView(emailEditText);
        formLayout.addView(createErrorView(EMAIL_KEY));
    }

    /**
     * 创建第二个表单
     */
    private void populateSecondForm(LinearLayout formLayout) {
        formLayout.addView(createTitle(mContext
                .getString(R.string.account_create_password_title)));
        EditText passwordEditText = createEditText(
                mContext.getString(R.string.account_create_password_hint),
                InputType.TYPE_CLASS_TEXT, EditorInfo.IME_ACTION_DONE, false,
                PASSWORD_KEY);
        //设置输入框文字展示形式
        passwordEditText.setTransformationMethod(new PasswordTransformationMethod());

        formLayout.addView(passwordEditText);
        formLayout.addView(createErrorView(PASSWORD_KEY));

        formLayout.addView(createTitle(mContext
                .getString(R.string.account_create_gender_title)));
        Spinner spinner = new Spinner(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.bottomMargin = 17;
        spinner.setLayoutParams(params);

        final ArrayAdapter<CharSequence> adapter = ArrayAdapter
                .createFromResource(mContext, R.array.sexes_array,
                        android.R.layout.simple_spinner_item);
        spinner.setAdapter(adapter);
        spinner.setPrompt(mContext
                .getString(R.string.account_create_sex_spinner_prompt));
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

            @Override
            public void onItemSelected(AdapterView<?> parent, View view,
                                       int pos, long id) {

                mFormData.put(GENDER_KEY, adapter.getItem(pos).toString());
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0) {
            }
        });

        if (mFormData.get(GENDER_KEY) != null) {
            spinner.setSelection(adapter.getPosition(mFormData
                    .get(GENDER_KEY)));
        }

        formLayout.addView(spinner);

    }

    /**
     * 创建第三个表单
     */
    private void populateThirdForm(LinearLayout formLayout) {
        formLayout.addView(createTitle(mContext
                .getString(R.string.account_create_city_title)));
        EditText cityEditText = createEditText(
                mContext.getString(R.string.account_create_city_hint),
                InputType.TYPE_CLASS_TEXT, EditorInfo.IME_ACTION_DONE, false,
                CITY_KEY);

        if (mFormData.get(CITY_KEY) != null) {
            cityEditText.setText(mFormData.get(CITY_KEY));
        }

        formLayout.addView(cityEditText);
        formLayout.addView(createErrorView(CITY_KEY));

        formLayout.addView(createTitle(mContext
                .getString(R.string.account_create_country_title)));

        Spinner spinner = new Spinner(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.bottomMargin = 17;
        spinner.setLayoutParams(params);

        final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                mContext, android.R.layout.simple_spinner_item,
                Countries.COUNTRIES);
        spinner.setAdapter(adapter);
        spinner.setPrompt(mContext
                .getString(R.string.account_create_country_spinner_prompt));
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

            @Override
            public void onItemSelected(AdapterView<?> parent, View view,
                                       int pos, long id) {

                mFormData.put(COUNTRY_KEY, Countries.COUNTRY_CODES[pos]);
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0) {
            }
        });

        if (mFormData.get(COUNTRY_KEY) != null) {
            List<String> array = Arrays.asList(Countries.COUNTRY_CODES);
            spinner.setSelection(array.indexOf(mFormData.get(COUNTRY_KEY)));
        }

        formLayout.addView(spinner);
    }

    /**
     * 创建第四个表单
     */
    private void populateFourthForm(LinearLayout formLayout) {
        formLayout.addView(createTitle(mContext
                .getString(R.string.account_create_zip_title)));
        EditText zipEditText = createEditText(
                mContext.getString(R.string.account_create_zip_hint),
                InputType.TYPE_CLASS_TEXT, EditorInfo.IME_ACTION_GO, true,
                ZIP_KEY);

        if (mFormData.get(ZIP_KEY) != null) {
            zipEditText.setText(mFormData.get(ZIP_KEY));
        }

        formLayout.addView(zipEditText);
        formLayout.addView(createErrorView(ZIP_KEY));
    }

    /**
     * 创建标题
     */
    private TextView createTitle(String text) {
        TextView ret = new TextView(mContext);
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        ret.setLayoutParams(params);
        ret.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
        ret.setTextColor(Color.BLACK);
        ret.setText(text);

        return ret;
    }

    /**
     * 创建输入框
     */
    private EditText createEditText(String hint, int inputType,
                                    int imeOption, boolean shouldMoveToNext, final String key) {

        EditText ret = new EditText(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        ret.setLayoutParams(params);
        ret.setHint(hint);

        ret.setInputType(inputType);//设置输入框输入模式
        ret.setImeOptions(imeOption);//设置软键盘模式

        if (shouldMoveToNext) {
            ret.setOnEditorActionListener(new TextView.OnEditorActionListener() {

                @Override
                public boolean onEditorAction(TextView v, int actionId,
                                              KeyEvent event) {
                    if (mDelegate != null) {
                        //软键盘中的Next按钮
                        if (EditorInfo.IME_ACTION_NEXT == actionId) {
                            mDelegate.scroll(CreateAccountDelegate.FORWARD);
                        } else {
                            processForm();
                        }

                        return true;
                    } else {
                        return false;
                    }
                }
            });
        }

        ret.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start, int before,
                                      int count) {
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start,
                                          int count, int after) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                mFormData.put(key, s.toString());
            }
        });

        return ret;
    }

    /**
     * 提交表单
     */
    private void processForm() {
        if (mDelegate != null) {
            Account account = new Account();
            account.setCity(mFormData.get(CITY_KEY));
            account.setCountry(mFormData.get(COUNTRY_KEY));
            account.setEmail(mFormData.get(EMAIL_KEY));
            account.setGender(mFormData.get(GENDER_KEY));
            account.setName(mFormData.get(FULL_NAME_KEY));
            account.setPassword(mFormData.get(PASSWORD_KEY));
            account.setPostalCode(mFormData.get(ZIP_KEY));

            mDelegate.processForm(account);
        }
    }

    /**
     * 创建错误提醒文字
     */
    private TextView createErrorView(String key) {
        TextView ret = new TextView(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.topMargin = 10;
        params.bottomMargin = 10;
        ret.setLayoutParams(params);
        ret.setTextColor(Color.RED);
        ret.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);

        HashMap<String, String> errors = mAccount.getErrors();
        if (errors != null && errors.containsKey(key)) {
            ret.setText(errors.get(key));
            ret.setVisibility(View.VISIBLE);
        } else {
            ret.setVisibility(View.INVISIBLE);
        }

        return ret;
    }

    /**
     * 展示错误信息
     */
    public void showErrors(Account account) {
        mAccount = account;
        mFormData.clear();
        mFormData.put(FULL_NAME_KEY, mAccount.getName());
        mFormData.put(EMAIL_KEY, mAccount.getEmail());
        mFormData.put(GENDER_KEY, mAccount.getGender());
        mFormData.put(CITY_KEY, mAccount.getCity());
        mFormData.put(COUNTRY_KEY, mAccount.getCountry());
        mFormData.put(ZIP_KEY, mAccount.getPostalCode());

        notifyDataSetChanged();
    }
}

我们还有一个模块没有讲到,那就是用来实现CreateAccountDelegate接口的CreateAccountActivity类,代码如下:

package com.example.huangfei.hack2;

import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.widget.Gallery;
import android.widget.Toast;

import com.example.huangfei.hack2.model.Account;

import java.util.HashMap;

/**
 * Created by huangfeihong on 2015/11/19.
 * 用于跟踪用户当前所在的页面,并处理页面跳转的逻辑
 */
public class CreateAccountActivity extends Activity implements
        CreateAccountAdapter.CreateAccountDelegate {
    private Gallery mGallery;
    private CreateAccountAdapter mAdapter;
    private int mGalleryPosition;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.create_account);
        mGallery = (Gallery) findViewById(R.id.create_account_gallery);

        mAdapter = new CreateAccountAdapter(this);
        mGallery.setAdapter(mAdapter);
        mGalleryPosition = 0;
    }

    /**
     * 在onResume()方法中将当前Activity设置为Adapter的委托,
     * 并且在onPause()方法中将委托置空
     */
    @Override
    protected void onResume() {
        super.onResume();
        mAdapter.setDelegate(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mAdapter.setDelegate(null);
    }

    /**
     * 重写onBackPressed()方法,用于返回上一个页面
     */
    @Override
    public void onBackPressed() {
        if (mGalleryPosition > 0) {
            scroll(BACKWARD);
        } else {
            super.onBackPressed();
        }
    }

    /**
     * 在该方法中,Activity可以根据参数将Gallery移动到下一个页面或者上一个页面中
     * 遗憾的是,我们无法为Gallery控件的页面切换添加动画效果。我唯一想到方法是发送
     * KeyEvent.KEYCODE_DPAD_RIGHT事件,虽然这是投机取巧,却也管用。
     */
    @Override
    public void scroll(int type) {
        switch (type) {
            case FORWARD:
            default:

                if (mGalleryPosition < mGallery.getCount() - 1) {
                    mGallery.onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT, new KeyEvent(0,
                            0));
                    mGalleryPosition++;
                }
                break;

            case BACKWARD:
                if (mGalleryPosition > 0) {
                    mGallery.onKeyDown(KeyEvent.KEYCODE_DPAD_LEFT, new KeyEvent(0,
                            0));
                    mGalleryPosition--;
                }
        }
    }

    @Override
    public void processForm(Account account) {
        HashMap<String, String> errors = account.getErrors();
        String email = account.getEmail();

        if (TextUtils.isEmpty(email)) {
            errors.put(CreateAccountAdapter.EMAIL_KEY, "E-mail is empty");
        } else if (email.toLowerCase().equals("me@my.com")) {
            errors.put(CreateAccountAdapter.EMAIL_KEY,
                    "E-mail is already taken");
        }

        if (errors.isEmpty()) {
            Toast.makeText(this, "Form ok!", Toast.LENGTH_SHORT).show();
            finish();
        } else {
            mAdapter.showErrors(account);
            mGallery.setSelection(0);
            mGalleryPosition = 0;
        }
    }


}

使用Gallery控件创建向导表单可以简化用户填写较长表单的流程。将表单放在不同页面中,并且利用Gallery控件的默认动画添加悦目的效果,可以使用户在填写表单的过程中更愉悦些。

根据需要,我们其实也可以使用ViewPager 开发相同的功能,只是其Adapter返回的不是View而是Fragment。

代码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值