博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从源码角度一步一步来修改PreferenceActivity界面
阅读量:7129 次
发布时间:2019-06-28

本文共 27373 字,大约阅读时间需要 91 分钟。

      

  PreferenceActivity给我们封装好了一个数据存储对象,我们只需要在xml文件中写上控件即可完成简单的设置界面。但是系统提供的设置界面十分的简陋,要想做的好看必须要自己来进行修改。本文就是一步一步教大家如何定义自己的PreferenceActivity界面。

 

一、创建模块一(选择模块组)

 

先在res/xml文件夹中定义一个customer_prefs.xml文件(名字自定),建立根节点

  ……

 

我们的每一个选项组都是一个PreferenceCategory,所以我们在节点中定义这个标签。

 我们注意到在PreferenceCategory节点中有android:layout属性,这个属性就是定义这个模块组的头部视图的。在这个layout中的textview的id必须是安卓自己的id,也就是title,这样我们在xml中设置的title就会对应到这个布局的文字上,才能有效!

prefs_category_widget

 对应到布局就是->

接下来我们在PreferenceCategory里面写上了我们的控件,这里我们放的是一个checkbox,一个单选list,一个多选List

title:控件上的主文字

defaultValue:默认的值

icon:左边的图标

key:这个控件的key(必须有),系统是通过这个key来存放数据的

layout:这个控件的布局文件

widgetLayout:这个控件右边的一个布局中的布局文件,就是上面layout布局中,id为widget_frame的布局中要填入的布局。我在checkboxpreference中就定义了这个属性,放上了自定义的checkbox。这是布局文件:

checkbox_prefs_widget.xml

 

 

summary:控件的摘要(说明)文字

entries:在单选/多选列表中,显示的选项信息。功能:给用户看

entryValues:在单选/多选列表中,选项信息对应的值。系统最后户把这个控件的key和这个值,以键值对的形式进行存放。

胖丁
布丁
皮卡丘
white
blue
gray
pangding
buding
pikaqiu
第一个模块的全部布局

这里面用到了我自定的两个类,一个是单选菜单,一个是多选菜单。其实我就是继承了原本的类,然后让原本的summary中显示我们选中的值而已。下面是两个类的代码,很简单。

MyListPreference

package com.kale.preference;import android.content.Context;import android.preference.ListPreference;import android.util.AttributeSet;import android.view.View;public class MyListPreference extends ListPreference{    public MyListPreference(Context context, AttributeSet attrs) {        super(context, attrs);    }    public MyListPreference(Context context) {        this(context, null);    }        @Override    protected void onBindView(View view) {        super.onBindView(view);        setSummary(getEntry() == null?getSummary():getEntry());        //setSummary(getEntry());    }}

 

MyMultiSelectListPreference

package com.kale.preference;import java.util.Set;import android.content.Context;import android.preference.MultiSelectListPreference;import android.util.AttributeSet;import android.view.View;public class MyMultiSelectListPreference extends MultiSelectListPreference {    public MyMultiSelectListPreference(Context context) {        super(context);    }    public MyMultiSelectListPreference(Context context, AttributeSet attrs) {        super(context, attrs);    }    /**     * @return 列表选中状态的数组     */    private boolean[] getSelectedItems() {        final CharSequence[] entries = getEntryValues();        final int entryCount = entries.length;        final Set
values = getValues(); boolean[] result = new boolean[entryCount]; for (int i = 0; i < entryCount; i++) { result[i] = values.contains(entries[i].toString()); } return result; } private void updateSummary() { setSummary(" "); CharSequence[] c = getEntries();// 得到列表值数组 String str = (String) getSummary();// 得到列表选择状态数组 for (int i = 0; i < getSelectedItems().length; i++) { if (getSelectedItems()[i]) { str += (String) c[i] + ","; } } str = str.substring(0, str.length() - 1);// 去除最后一个逗号 // 设置摘要的内容 setSummary(str); } @Override protected void onBindView(View view) { super.onBindView(view); int i; for (i = 0; i < getSelectedItems().length; i++) { if (getSelectedItems()[i] == true) { updateSummary(); break; } } if (i == getSelectedItems().length) { setSummary(""); } } @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); int i; for (i = 0; i < getSelectedItems().length; i++) { if (getSelectedItems()[i] == true) { updateSummary(); break; } } if (i == getSelectedItems().length) { setSummary(""); } }}

 

于是我们第一个模块组就大功告成了!

 

--------------------------------------------------------------------------------------------

 

二、创建模块二(特殊模块组)

 

 

这个模块中有两个新的属性

negativeButtonText:设置对话框中取消按钮的文字

positiveButtonText:设置对话框中确定按钮的文字,点击确定后会将值保存下来

通过前面的铺垫,我们现在可以直接看懂以下的代码了

 

MyEditTextPreference,MySeekBarDialogPreference,MySeekBarPreference都是我自己定义的类,功能还是将所输入的/所选择的值显示到summary中。

MyEditTextPreference

package com.kale.preference;import android.content.Context;import android.preference.EditTextPreference;import android.util.AttributeSet;import android.view.View;public class MyEditTextPreference extends EditTextPreference {    public MyEditTextPreference(Context context, AttributeSet attrs,            int defStyle) {        super(context, attrs, defStyle);    }    public MyEditTextPreference(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    protected void onBindView(View view) {        super.onBindView(view);        setSummary(getText() == null?getSummary():getText());    }}

 

MySeekBarDialogPreference

/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.kale.preference;import android.content.Context;import android.content.SharedPreferences;import android.graphics.drawable.Drawable;import android.preference.DialogPreference;import android.util.AttributeSet;import android.view.View;import android.widget.ImageView;import android.widget.SeekBar;import android.widget.TextView;import android.widget.SeekBar.OnSeekBarChangeListener;import com.kale.shared.R;public class MySeekBarDialogPreference extends DialogPreference implements        OnSeekBarChangeListener {    //private static final String TAG = "SeekBarDialogPreference";    private Drawable mMyIcon;    private TextView value;    private SeekBar seekBar;    // 如果要修改最大值和最小值的话,那么可以在这里修改。或者是调用函数setMax、setMin    private int mMax = 100, mMin = 0;    private int mProgress;    public MySeekBarDialogPreference(Context context, AttributeSet attrs) {        super(context, attrs);        setDialogLayoutResource(R.layout.seekbar_dialog_prefs_widget);        // Steal the XML dialogIcon attribute's value        mMyIcon = getDialogIcon();        setDialogIcon(null);    }    public MySeekBarDialogPreference(Context context) {        this(context, null);    }    public void setMax(int max) {        if (max != mMax) {            mMax = max;            notifyChanged();        }    }    public void setMin(int min) {        if (min != mMin) {            mMin = min;            notifyChanged();        }    }    /**     * Saves the progress to the {
@link SharedPreferences}. * * @param text * The progress to save */ public void setProgress(int progress) { final boolean wasBlocking = shouldDisableDependents(); mProgress = progress; persistInt(mProgress); final boolean isBlocking = shouldDisableDependents(); if (isBlocking != wasBlocking) { notifyDependencyChange(isBlocking); } } /** * Gets the text from the {
@link SharedPreferences}. * * @return The current preference value. */ public int getProgress() { return mProgress; } @Override protected void onBindView(View view) { super.onBindView(view); mProgress = getPersistedInt(getProgress()); setSummary(String.valueOf(mProgress)); } @Override protected void onBindDialogView(View view) { super.onBindDialogView(view); final ImageView iconView = (ImageView) view.findViewById(android.R.id.icon); value = (TextView)view.findViewById(R.id.value); if (mMyIcon != null) { iconView.setImageDrawable(mMyIcon); } else { iconView.setVisibility(View.GONE); } seekBar = getSeekBar(view); seekBar.setMax(mMax-mMin); seekBar.setProgress(mProgress); seekBar.setOnSeekBarChangeListener(this); value.setText(String.valueOf(mProgress)); } @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); // 如果没按下确定按钮就返回null if (!positiveResult) { return; } if (shouldPersist()) { persistInt(mProgress); setSummary(String.valueOf(mProgress)); } // 提交数据 notifyChanged(); } protected static SeekBar getSeekBar(View dialogView) { return (SeekBar) dialogView.findViewById(R.id.seekbar); } @Override public CharSequence getSummary() { String summary = super.getSummary().toString(); int value = getPersistedInt(mProgress); return String.format(summary, value); } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { value.setText(String.valueOf(progress)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO 自动生成的方法存根 } @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO 自动生成的方法存根 mProgress = seekBar.getProgress() + mMin; }}

 

用到的布局文件:seekbar_dialog_prefs_widget.xml

 

 

MySeekBarPreference

/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.kale.preference;import android.content.Context;import android.os.Parcel;import android.os.Parcelable;import android.preference.Preference;import android.util.AttributeSet;import android.view.View;import android.widget.SeekBar;import android.widget.SeekBar.OnSeekBarChangeListener;import android.widget.TextView;import com.kale.shared.R;public class MySeekBarPreference extends Preference implements OnSeekBarChangeListener {    private TextView value;    private int mProgress;    private int mMax = 100;    private boolean mTrackingTouch;        public MySeekBarPreference(            Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        setMax(mMax);        setLayoutResource(R.layout.seekbar_prefs);    }    public MySeekBarPreference(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public MySeekBarPreference(Context context) {        this(context, null);    }        @Override    protected void onBindView(View view) {        super.onBindView(view);        SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar);        seekBar.setMax(mMax);        seekBar.setProgress(mProgress);        seekBar.setEnabled(isEnabled());        seekBar.setOnSeekBarChangeListener(this);        value = (TextView)view.findViewById(R.id.value);        value.setText(String.valueOf(mProgress));    }    @Override    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {        setProgress(restoreValue ? getPersistedInt(mProgress): (Integer) defaultValue);    }    public void setMax(int max) {        if (max != mMax) {            mMax = max;            notifyChanged();        }    }    public void setProgress(int progress) {        setProgress(progress, true);    }    private void setProgress(int progress, boolean notifyChanged) {        if (progress > mMax) {            progress = mMax;        }        if (progress < 0) {            progress = 0;        }        if (progress != mProgress) {            mProgress = progress;            persistInt(progress);            if (notifyChanged) {                notifyChanged();            }        }    }    public int getProgress() {        return mProgress;    }    /**     * Persist the seekBar's progress value if callChangeListener     * returns true, otherwise set the seekBar's progress to the stored value     */    void syncProgress(SeekBar seekBar) {        int progress = seekBar.getProgress();        if (progress != mProgress) {            if (callChangeListener(progress)) {                setProgress(progress, false);            } else {                seekBar.setProgress(mProgress);            }        }    }    @Override    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                if (fromUser) {            System.err.println("now value = "+progress);                    }        if (fromUser && !mTrackingTouch) {            syncProgress(seekBar);        }    }    @Override    public void onStartTrackingTouch(SeekBar seekBar) {        mTrackingTouch = true;    }    @Override    public void onStopTrackingTouch(SeekBar seekBar) {        mTrackingTouch = false;        if (seekBar.getProgress() != mProgress) {            syncProgress(seekBar);            value.setText(seekBar.getProgress()+"");        }    }    @Override    protected Parcelable onSaveInstanceState() {        /*         * Suppose a client uses this preference type without persisting. We         * must save the instance state so it is able to, for example, survive         * orientation changes.         */        final Parcelable superState = super.onSaveInstanceState();        if (isPersistent()) {            // No need to save instance state since it's persistent            return superState;        }        // Save the instance state        final SavedState myState = new SavedState(superState);        myState.progress = mProgress;        myState.max = mMax;        return myState;    }    @Override    protected void onRestoreInstanceState(Parcelable state) {        if (!state.getClass().equals(SavedState.class)) {            // Didn't save state for us in onSaveInstanceState            super.onRestoreInstanceState(state);            return;        }        // Restore the instance state        SavedState myState = (SavedState) state;        super.onRestoreInstanceState(myState.getSuperState());        mProgress = myState.progress;        mMax = myState.max;        notifyChanged();    }    /**     * SavedState, a subclass of {
@link BaseSavedState}, will store the state * of MyPreference, a subclass of Preference. *

* It is important to always call through to super methods. */ private static class SavedState extends BaseSavedState { int progress; int max; public SavedState(Parcel source) { super(source); // Restore the click counter progress = source.readInt(); max = source.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); // Save the click counter dest.writeInt(progress); dest.writeInt(max); } public SavedState(Parcelable superState) { super(superState); } }}

所用到的布局文件:seekbar_prefs.xml

于是,我们就完成了第二个模块的设置。

 

--------------------------------------------------------------------------------------------

 

三、第三个模块组的(触发模块组)

这个组里面有一个点击触发动作和自定义preference

 

intent模块中我们设置一个action和data属性,这样点击它后,与之对应的Activity将被启动。这里实现的效果是启动浏览器来展示网页。

 

我们还自定义了一个preference,用来做一些特殊的操作。这个preference不用写什么自定义类,直接在这个的Activity中找到它既可。

package com.kale.shared;import android.content.Intent;import android.content.SharedPreferences;import android.os.Bundle;import android.preference.Preference;import android.preference.Preference.OnPreferenceClickListener;import android.preference.PreferenceActivity;import android.widget.Toast;public class MainActivity extends PreferenceActivity implements        OnPreferenceClickListener {    /** 自定义布局 **/    Preference myPrefers = null;    @SuppressWarnings("deprecation")    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // 设置背景图,给activity设置后。所有fragment的背景都会改了,十分方便!        // getWindow().setBackgroundDrawable(getResources().getDrawable(R.color.bgColor));        // setContentView(R.layout.activity_main); 这里就不能设置布局了        setContentView(R.layout.prefs_list_content);        addPreferencesFromResource(R.xml.customer_prefs);        // 初始化控件        myPrefers = findPreference("my_prefs_key");        myPrefers.setSummary("可以自定义布局和点击事件");        myPrefers.setOnPreferenceClickListener(this);    }    @Override    public boolean onPreferenceClick(Preference preference) {        if (preference == myPrefers) {            Toast.makeText(MainActivity.this, "自定义Preference被按下",                    Toast.LENGTH_SHORT).show();        }         return false;    }        }

 

现在,这个模块也定义好了。

 

--------------------------------------------------------------------------------------------

四、子父依赖模块组

 

这个模块存在一个依赖关系,子控件必须在父控件开启的时候才能进行操作,否则不可用。也就是说只有上面的父控件的开关开启,下面的子控件的开关才能用。

这里的关键属性是:android:dependency="parent_checkbox_preference",设置这个属性的控件表明自己是依托于parent_checkbox_preference这个控件的

 

--------------------------------------------------------------------------------------------

 

五、二级菜单模块组

点击任何一个栏目,就会跳到另一个Activity中进行操作,最后系统会将选择的值返回到这个Activity中。

 

下面这种嵌套产生二级菜单的方式是系统给出的,很简单,但是扩展性很差。↓

    

 

下面这个就是一个intent,点击后跳转到指定的Activity中去。是intent的隐式跳转。↓

    

我定义了这样一个Activity来相应这个intent。

需要注意的是:这里面category节点必须定义,否则会出现找不到action的情况!

下面这种其实就是自定义了preference,点击后跳转到一个Activity中去。↓

    

MainActivity

package com.kale.shared;import android.content.Intent;import android.content.SharedPreferences;import android.os.Bundle;import android.preference.Preference;import android.preference.Preference.OnPreferenceClickListener;import android.preference.PreferenceActivity;import android.widget.Toast;public class MainActivity extends PreferenceActivity implements        OnPreferenceClickListener {    /** 自定义布局 **/    Preference myPrefers = null;    Preference getValueSingPrefs,getValueMultiPrefs;    SharedPreferences sp;    SharedPreferences.Editor editor;    @SuppressWarnings("deprecation")    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // 设置背景图,给activity设置后。所有fragment的背景都会改了,十分方便!        // getWindow().setBackgroundDrawable(getResources().getDrawable(R.color.bgColor));        // setContentView(R.layout.activity_main); 这里就不能设置布局了        sp = getSharedPreferences("kaleShared", MODE_PRIVATE);        editor = sp.edit();        editor.putString("KEY", "value");        editor.commit();        if (sp.contains("KEY")) {            System.out.println("have a key");        }        setContentView(R.layout.prefs_list_content);        addPreferencesFromResource(R.xml.customer_prefs);        // 初始化控件        initPrefers();        myPrefers.setSummary("可以自定义布局和点击事件");        myPrefers.setOnPreferenceClickListener(this);        getValueSingPrefs.setOnPreferenceClickListener(this);        getValueMultiPrefs.setOnPreferenceClickListener(this);    }    @Override    public boolean onPreferenceClick(Preference preference) {        if (preference == myPrefers) {            Toast.makeText(MainActivity.this, "自定义Preference被按下",                    Toast.LENGTH_SHORT).show();        } else if (preference == getValueSingPrefs) {            Intent intent = new Intent(MainActivity.this, SingleActivity.class);            startActivityForResult(intent, 100);        }        else if (preference == getValueMultiPrefs) {            Intent intent = new Intent(MainActivity.this, MultiActivity.class);            startActivityForResult(intent, 120);        }        return false;    }        @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        //如果返回码表示RESULT_OK,那么说明是从SecondActivity返回的intent        if (resultCode == RESULT_OK) {            //如果是100表示是返回给启动activity的intent(这里是我们的getValuePreference)的值            switch (requestCode) {            case 100:                getValueSingPrefs.setSummary(data.getExtras().getString("osNameKey"));                break;            case 120:                getValueMultiPrefs.setSummary(data.getExtras().getString("languageKey"));                break;            default:                break;            }                        }    }        @SuppressWarnings("deprecation")    private void initPrefers() {        myPrefers = findPreference("my_prefs_key");        getValueSingPrefs = findPreference("getValue_single_prefers");        getValueMultiPrefs = findPreference("getValue_multi_prefers");    }}

 

跳转的界面如下:

  上面是单选列表,下面是多选列表。我已经写好了一个类,大家只需要找到这个activity中的所有checkboxpreference,然后通过一个方法就能设置为单选组和多选组了。很简单吧~

//添加到单选列表中

addToSingleChoiceList(android,ios,wp);
// 添加到多选列表中
addToMultiChoiceList("languageKey",java, c, js);

package com.kale.shared;import android.os.Bundle;import android.preference.CheckBoxPreference;import com.kale.preference.ChoicePrefsActivity;/** * @author:Jack Tony * @tips  :展现多选列表的界面 * 初始化后将preference添加到多选列表中即可 * addToSingleChoiceList(android,ios,wp); * @date  :2014-8-6 */@SuppressWarnings("deprecation")public class MultiActivity extends ChoicePrefsActivity {    CheckBoxPreference android,ios,wp;    CheckBoxPreference java, c, js;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        getWindow().setBackgroundDrawableResource(R.color.bgColor);        setContentView(R.layout.prefs_list_content);        addPreferencesFromResource(R.xml.second_level_prefs);        initPreference();        //添加到单选列表中        addToSingleChoiceList(android,ios,wp);        // 添加到多选列表中        addToMultiChoiceList("languageKey",java, c, js);    }    private void initPreference() {        android = (CheckBoxPreference) findPreference("android_prefs");        ios = (CheckBoxPreference) findPreference("ios_prefs");        wp = (CheckBoxPreference) findPreference("wp_prefs");                java = (CheckBoxPreference) findPreference("java_prefs");        c = (CheckBoxPreference) findPreference("c_prefs");        js = (CheckBoxPreference) findPreference("js_prefs");            }}

 

 

在MainActivity中我用onActivityResult()取得在二级菜单中选中的值,并且赋值给summary。

package com.kale.shared;import android.content.Intent;import android.content.SharedPreferences;import android.os.Bundle;import android.preference.Preference;import android.preference.Preference.OnPreferenceClickListener;import android.preference.PreferenceActivity;import android.widget.Toast;public class MainActivity extends PreferenceActivity implements        OnPreferenceClickListener {    /** 自定义布局 **/    Preference myPrefers = null;    Preference getValueSingPrefs,getValueMultiPrefs;    SharedPreferences sp;    SharedPreferences.Editor editor;    @SuppressWarnings("deprecation")    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // 设置背景图,给activity设置后。所有fragment的背景都会改了,十分方便!        // getWindow().setBackgroundDrawable(getResources().getDrawable(R.color.bgColor));        // setContentView(R.layout.activity_main); 这里就不能设置布局了        sp = getSharedPreferences("kaleShared", MODE_PRIVATE);        editor = sp.edit();        editor.putString("KEY", "value");        editor.commit();        if (sp.contains("KEY")) {            System.out.println("have a key");        }        setContentView(R.layout.prefs_list_content);        addPreferencesFromResource(R.xml.customer_prefs);        // 初始化控件        initPrefers();        myPrefers.setSummary("可以自定义布局和点击事件");        myPrefers.setOnPreferenceClickListener(this);        getValueSingPrefs.setOnPreferenceClickListener(this);        getValueMultiPrefs.setOnPreferenceClickListener(this);    }    @Override    public boolean onPreferenceClick(Preference preference) {        if (preference == myPrefers) {            Toast.makeText(MainActivity.this, "自定义Preference被按下",                    Toast.LENGTH_SHORT).show();        } else if (preference == getValueSingPrefs) {            Intent intent = new Intent(MainActivity.this, SingleActivity.class);            startActivityForResult(intent, 100);        }        else if (preference == getValueMultiPrefs) {            Intent intent = new Intent(MainActivity.this, MultiActivity.class);            startActivityForResult(intent, 120);        }        return false;    }        @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        //如果返回码表示RESULT_OK,那么说明是从SecondActivity返回的intent        if (resultCode == RESULT_OK) {            //如果是100表示是返回给启动activity的intent(这里是我们的getValuePreference)的值            switch (requestCode) {            case 100:                getValueSingPrefs.setSummary(data.getExtras().getString("osNameKey"));                break;            case 120:                getValueMultiPrefs.setSummary(data.getExtras().getString("languageKey"));                break;            default:                break;            }                        }    }        @SuppressWarnings("deprecation")    private void initPrefers() {        myPrefers = findPreference("my_prefs_key");        getValueSingPrefs = findPreference("getValue_single_prefers");        getValueMultiPrefs = findPreference("getValue_multi_prefers");    }}

 

好啦,主要的讲解就到这里,下面附上源码。

源码下载:

 

转载地址:http://ishel.baihongyu.com/

你可能感兴趣的文章
我是如何破解你的WINDOWS密码的 ?(1)
查看>>
SQL Server: Top 10 Secrets of a SQL Server Expert
查看>>
loop循环
查看>>
laravel完美部署与六种解决报错高效方法
查看>>
iscsi多路径客户端的配置
查看>>
Ubuntu启动器快捷方式
查看>>
dhcp在企业网中的应用
查看>>
悠然推荐:你的架构是如何一步步腐化的?
查看>>
网页自动刷新
查看>>
信息安全从业人员的面试记录(持续更新,直到入职)
查看>>
mysql启动之:报错解决办法
查看>>
inode 索引节点和软硬链接
查看>>
文本处理工具基础(grep系、sed、awk等)
查看>>
centOS 安装mp4box
查看>>
导入org.apache.poi.xssf 读取excel
查看>>
Could not load file or assembly 'System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral..
查看>>
SpringBoot注入Mapper提示Could not autowire. No beans of 'xxxMapper' type found错误
查看>>
教你拉筋的方法
查看>>
WP老杨解迷:如何营造让人花钱的游戏
查看>>
3673: 可持久化并查集 by zky
查看>>