說到字母索引欄纳猫,在聯(lián)系人模塊經(jīng)常會(huì)見到球及。iOS中這種控件是自帶的芋忿,安卓中是沒有的,所以只能自定義解決了绒北。
需求分析
- 拖動(dòng)索引欄時(shí)。選中狀態(tài)
- 字母欄背景加深
- 文字顏色變白色
- 懸浮字母提示顯示
- 松手時(shí)察署,非選中狀態(tài)
- 字母欄背景透明
- 文字顏色變黑色
- 懸浮字母提示隱藏
需要自定義的屬性
- 選中時(shí)字體顏色
- 未選中時(shí)字體顏色
- 選中時(shí)闷游,索引欄背景顏色
- 非選中時(shí),索引欄背景顏色
提供的回調(diào)
選中和未選中時(shí)贴汪,回調(diào)位置和字母文字脐往。
索引條定義步驟(注釋上已經(jīng)寫得很清楚,就不再一步步贅述了)
- 自定義需要的屬性
<declare-styleable name="SlideBar">
<!-- 選中時(shí)扳埂,文字顏色 -->
<attr name="slb_select_txt_color" format="color|reference" />
<!-- 非選中時(shí)业簿,文字顏色 -->
<attr name="slb_un_select_txt_color" format="color|reference" />
<!-- 選中時(shí),滑動(dòng)條背景顏色 -->
<attr name="slb_select_bg_color" format="color|reference" />
<!-- 非選中時(shí)阳懂,滑動(dòng)條背景顏色 -->
<attr name="slb_un_select_bg_color" format="color|reference" />
</declare-styleable>
- 自定義View梅尤,繼承View,獲取設(shè)置的自定義屬性岩调,配置畫筆等
public class SlideBar extends View {
/**
* 默認(rèn)選中時(shí)巷燥,文字顏色
*/
private final int mDefaultSelectTextColor = Color.parseColor("#FFFFFF");
/**
* 默認(rèn)未選中時(shí),文字顏色
*/
private final int mDefaultUnSelectTextColor = Color.parseColor("#202020");
/**
* 默認(rèn)選中時(shí)号枕,滑動(dòng)條背景顏色
*/
private final int mDefaultSelectBgColor = Color.parseColor("#66202020");
/**
* 默認(rèn)未選中時(shí)缰揪,滑動(dòng)條背景顏色
*/
private final int mDefaultUnSelectBgColor = Color.parseColor("#00000000");
/**
* 字母表
*/
private String[] mLetter = new String[]{"#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
/**
* 是否拖動(dòng)索引條中
*/
private boolean mTouched = false;
/**
* 畫筆
*/
private Paint mPaint;
private int mSelectTextColor;
private int mUnSelectTextColor;
private int mSelectBgColor;
private int mUnSelectBgColor;
public SlideBar(Context context) {
super(context);
init(context, null);
}
public SlideBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SlideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
initAttrs(context, attrs);
initPaint();
}
private void initAttrs(Context context, @Nullable AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlideBar);
//選中時(shí)文字的顏色
mSelectTextColor = array.getColor(R.styleable.SlideBar_slb_select_txt_color, mDefaultSelectTextColor);
//未選中時(shí)文字的顏色
mUnSelectTextColor = array.getColor(R.styleable.SlideBar_slb_un_select_txt_color, mDefaultUnSelectTextColor);
//選中時(shí),滑動(dòng)條背景顏色
mSelectBgColor = array.getColor(R.styleable.SlideBar_slb_select_bg_color, mDefaultSelectBgColor);
//未選中時(shí)葱淳,滑動(dòng)條背景顏色
mUnSelectBgColor = array.getColor(R.styleable.SlideBar_slb_un_select_bg_color, mDefaultUnSelectBgColor);
array.recycle();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setTextSize(sp2px(getContext(), 11f));
if (mTouched) {
mPaint.setColor(mSelectTextColor);
} else {
mPaint.setColor(mUnSelectTextColor);
}
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mTextRect = new Rect();
}
}
- 定義回調(diào)接口
public interface OnSelectItemListener {
/**
* 選擇時(shí)回調(diào)
*
* @param position 選中的位置
* @param selectLetter 選中的字母
*/
void onItemSelect(int position, String selectLetter);
/**
* 松手取消選中時(shí)回調(diào)
*/
void onItemUnSelect();
}
public void setOnSelectItemListener(OnSelectItemListener listener) {
mListener = listener;
}
- 配置測(cè)量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec), heightMeasureSpec);
}
/**
* 測(cè)量本身的大小邀跃,這里只是測(cè)量寬度
*
* @param widthMeaSpec 傳入父View的測(cè)量標(biāo)準(zhǔn)
* @return 測(cè)量的寬度
*/
private int measureWidth(int widthMeaSpec) {
/*定義view的寬度*/
int width;
/*獲取當(dāng)前 View的測(cè)量模式*/
int mode = MeasureSpec.getMode(widthMeaSpec);
/*
* 獲取當(dāng)前View的測(cè)量值,這里得到的只是初步的值蛙紫,
* 我們還需根據(jù)測(cè)量模式來確定我們期望的大小
* */
int size = MeasureSpec.getSize(widthMeaSpec);
/*
* 如果,模式為精確模式
* 當(dāng)前View的寬度途戒,就是我們的size
* */
if (mode == MeasureSpec.EXACTLY) {
width = size;
} else {
/*否則的話我們就需要結(jié)合padding的值來確定*/
int desire = size + getPaddingLeft() + getPaddingRight();
if (mode == MeasureSpec.AT_MOST) {
width = Math.min(desire, size);
} else {
width = desire;
}
}
return width;
}
- 獲取到寬高時(shí)坑傅,計(jì)算每個(gè)字母的高度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
//每行的高度,計(jì)算平均分每個(gè)字母占的高度
mCellHeight = h / mLetter.length;
}
- 繪制文字和背景喷斋,觸摸和松手時(shí)改變變量來達(dá)到繪制不同的顏色
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//觸摸時(shí)改變背景顏色
if (mTouched) {
canvas.drawColor(mSelectBgColor);
mPaint.setColor(mSelectTextColor);
} else {
canvas.drawColor(mUnSelectBgColor);
mPaint.setColor(mUnSelectTextColor);
}
for (int i = 0; i < mLetter.length; i++) {
String text = mLetter[i];
//測(cè)量文字寬高
mPaint.getTextBounds(text, 0, text.length(), mTextRect);
int textWidth = mTextRect.width();
int textHeight = mTextRect.height();
//文字一半的寬度
float textHalfWidth = textWidth / 2.0f;
//字母文字的起點(diǎn)X坐標(biāo)唁毒,控件的寬度的一半再減去文字的一半
float x = (mWidth / 2.0f) - textHalfWidth;
//起點(diǎn)文字的Y坐標(biāo)
float y = (mCellHeight / 2.0f + textHeight / 2.0f + mCellHeight * i);
//畫文字
canvas.drawText(mLetter[i], x, y, mPaint);
}
}
- 處理觸摸和松手判斷,并進(jìn)行回調(diào)
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
//計(jì)算當(dāng)前觸摸的字母的位置
int index = (int) (y / mCellHeight);
//觸摸
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
mTouched = true;
if (index >= 0 && index < mLetter.length) {
if (mListener != null) {
mListener.onItemSelect(index, mLetter[index]);
}
} else {
return super.onTouchEvent(event);
}
} else {
//松手
mTouched = false;
if (mListener != null) {
mListener.onItemUnSelect();
}
}
//觸摸改變時(shí)星爪,不斷通知重繪來繪制索引條
invalidate();
return true;
}
界面布局
- RecyclerView列表
- 索引欄
- 字母提示(我們直接用個(gè)TextView浆西,設(shè)置背景,顯示隱藏即可)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/refresh_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<me.zh.indexslidebar.SlideBar
android:id="@+id/slide_bar"
android:layout_width="24dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:visibility="visible"
app:slb_select_bg_color="@android:color/darker_gray"
app:slb_select_txt_color="@android:color/white"
app:slb_un_select_bg_color="@android:color/transparent"
app:slb_un_select_txt_color="@color/colorPrimary" />
<TextView
android:id="@+id/check_letter"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="@drawable/bg_contact_letter_flow"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="40sp"
android:visibility="gone"
tools:text="A"
tools:visibility="visible" />
</FrameLayout>
具體使用
- 提供一組聯(lián)系人名字
/**
* 聯(lián)系人數(shù)組
*/
public static String[] mNames = new String[]{"宋江", "盧俊義", "吳用",
"公孫勝", "關(guān)勝", "林沖", "秦明", "呼延灼", "花榮", "柴進(jìn)", "李應(yīng)", "朱仝", "魯智深",
"武松", "董平", "張清", "楊志", "徐寧", "索超", "戴宗", "劉唐", "李逵", "史進(jìn)", "穆弘",
"雷橫", "李俊", "阮小二", "張橫", "阮小五", "張順", "阮小七", "楊雄", "石秀", "解珍",
"解寶", "燕青", "朱武", "黃信", "孫立", "宣贊", "郝思文", "韓滔", "彭玘", "單廷珪",
"魏定國(guó)", "蕭讓", "裴宣", "歐鵬", "鄧飛", "燕順", "楊林", "凌振", "蔣敬", "呂方",
"郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鮑旭", "樊瑞", "孔明", "孔亮", "項(xiàng)充",
"李袞", "金大堅(jiān)", "馬麟", "童威", "童猛", "孟康", "侯健", "陳達(dá)", "楊春", "鄭天壽",
"陶宗旺", "宋清", "樂和", "龔?fù)?, "丁得孫", "穆春", "曹正", "宋萬", "杜遷", "薛永", "施恩",
"周通", "李忠", "杜興", "湯隆", "鄒淵", "鄒潤(rùn)", "朱富", "朱貴", "蔡福", "蔡慶", "李立",
"李云", "焦挺", "石勇", "孫新", "顧大嫂", "張青", "孫二娘", "王定六", "郁保四", "白勝",
"時(shí)遷", "段景柱", "&張三", "11級(jí)李四", "12級(jí)小明"};
}
- 字母和字母條的位置映射
/**
* 用于保存聯(lián)系人首字母在列表的位置
*/
private HashMap<String, Integer> mLetterPositionMap = new HashMap<>();
- 配置好3個(gè)View(Rv使用自己熟悉的框架即可顽腾,這里使用的是MultiType)
public class MainActivity extends AppCompatActivity {
private RecyclerView vRefreshList;
/**
* 字母?jìng)?cè)滑欄
*/
private SlideBar mSlideBar;
/**
* 提示View
*/
private TextView vCheckLetterView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
bindView();
setData();
}
private void findView() {
mSlideBar = findViewById(R.id.slide_bar);
vRefreshList = findViewById(R.id.refresh_list);
vCheckLetterView = findViewById(R.id.check_letter);
}
private void bindView() {
//配置列表
vRefreshList.setLayoutManager(new LinearLayoutManager(this));
mListItems = new Items();
mListAdapter = new MultiTypeAdapter(mListItems);
//聯(lián)系人字母條目
mListAdapter.register(ContactLetterModel.class, new ContactLetterViewBinder());
//聯(lián)系人條目
mListAdapter.register(ContactModel.class, new ContactViewBinder());
vRefreshList.setAdapter(mListAdapter);
}
- 配置索引條回調(diào)近零,在選中時(shí)诺核,將列表滾動(dòng)到記錄的位置,有些字母在我們的字母表里面可能沒有對(duì)應(yīng)的久信,會(huì)找不到窖杀,所以需要判空
//配置索引條
mSlideBar.setOnSelectItemListener(new SlideBar.OnSelectItemListener() {
@Override
public void onItemSelect(int position, String selectLetter) {
if (vCheckLetterView.getVisibility() != View.VISIBLE) {
vCheckLetterView.setVisibility(View.VISIBLE);
}
vCheckLetterView.setText(selectLetter);
Integer letterStickyPosition = mLetterPositionMap.get(selectLetter);
//這里可能拿不到,因?yàn)椴⒉皇撬械淖帜嘎?lián)系人名字上都有
if (letterStickyPosition != null) {
vRefreshList.scrollToPosition(letterStickyPosition);
}
}
@Override
public void onItemUnSelect() {
vCheckLetterView.setVisibility(View.GONE);
}
});
- 根據(jù)姓名表裙士,構(gòu)造聯(lián)系人列表和記錄字母條位置條目入客,這里需要將姓名的第一個(gè)子的首字母,用到了一個(gè)拼音庫腿椎。
implementation 'com.github.promeg:tinypinyin:2.0.3'
先來將姓名表按姓名的第一個(gè)字的英文字母來排序桌硫,這樣是為了后面遍歷判斷字母,對(duì)同一組字母的條目的第一個(gè)條目插入一個(gè)字母條目啃炸。
List<String> nameList = Arrays.asList(mNames);
Collections.sort(nameList, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
char letter = Character.toUpperCase(Pinyin.toPinyin(o1.charAt(0)).charAt(0));
char nextLetter = Character.toUpperCase(Pinyin.toPinyin(o2.charAt(0)).charAt(0));
if (letter == nextLetter) {
return 0;
} else {
return letter - nextLetter;
}
}
});
- 遍歷姓名列表铆隘,構(gòu)造條目模型。怎么讓多個(gè)同英文字母的姓名分組前插入一個(gè)字母條目呢肮帐?很簡(jiǎn)單咖驮,每次遍歷時(shí)判斷是不是和上一個(gè)字母一致,不一致才添加一個(gè)训枢。記錄字母條目的位置托修,其實(shí)就是添加字母條目時(shí),獲取自己在列表數(shù)據(jù)集的位置恒界,因?yàn)槊看味继砑拥轿膊磕廊校灾苯尤×斜頂?shù)據(jù)集的最后一位的位置即可。
//字母
char letter = 0;
for (int i = 0; i < nameList.size(); i++) {
if (i == 0) {
//獲取文字第一個(gè)字母
String letterPinyin = Pinyin.toPinyin(nameList.get(i).charAt(0));
letter = Character.toUpperCase(letterPinyin.charAt(0));
mListItems.add(new ContactLetterModel(String.valueOf(letter)));
} else {
//如果下一個(gè)條目的首字母和上一個(gè)的不一樣十酣,則插入一條新的字母條目
String letterPinyin = Pinyin.toPinyin(nameList.get(i).charAt(0));
char nextLetter = Character.toUpperCase(letterPinyin.charAt(0));
if (nextLetter != letter) {
letter = nextLetter;
mListItems.add(new ContactLetterModel(String.valueOf(letter)));
//記錄字母條目的位置涩拙,后續(xù)拉動(dòng)字母選擇條時(shí)跳轉(zhuǎn)位置
mLetterPositionMap.put(String.valueOf(letter).toUpperCase(), mListItems.size() - 1);
}
}
//添加聯(lián)系人條目
mListItems.add(new ContactModel(nameList.get(i)));
}
mListAdapter.notifyDataSetChanged();
完整代碼
public class SlideBar extends View {
/**
* 默認(rèn)選中時(shí),文字顏色
*/
private final int mDefaultSelectTextColor = Color.parseColor("#FFFFFF");
/**
* 默認(rèn)未選中時(shí)耸采,文字顏色
*/
private final int mDefaultUnSelectTextColor = Color.parseColor("#202020");
/**
* 默認(rèn)選中時(shí)兴泥,滑動(dòng)條背景顏色
*/
private final int mDefaultSelectBgColor = Color.parseColor("#66202020");
/**
* 默認(rèn)未選中時(shí),滑動(dòng)條背景顏色
*/
private final int mDefaultUnSelectBgColor = Color.parseColor("#00000000");
/**
* 索引條寬度
*/
private int mWidth;
/**
* 字母表
*/
private String[] mLetter = new String[]{"#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
/**
* 文字區(qū)域虾宇,保存為成員變量是為了復(fù)用
*/
private Rect mTextRect;
/**
* 每個(gè)字母的高度
*/
private int mCellHeight;
/**
* 是否拖動(dòng)索引條中
*/
private boolean mTouched = false;
/**
* 選擇回調(diào)
*/
private OnSelectItemListener mListener;
/**
* 畫筆
*/
private Paint mPaint;
private int mSelectTextColor;
private int mUnSelectTextColor;
private int mSelectBgColor;
private int mUnSelectBgColor;
public SlideBar(Context context) {
super(context);
init(context, null);
}
public SlideBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SlideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
initAttrs(context, attrs);
initPaint();
}
private void initAttrs(Context context, @Nullable AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlideBar);
//選中時(shí)文字的顏色
mSelectTextColor = array.getColor(R.styleable.SlideBar_slb_select_txt_color, mDefaultSelectTextColor);
//未選中時(shí)文字的顏色
mUnSelectTextColor = array.getColor(R.styleable.SlideBar_slb_un_select_txt_color, mDefaultUnSelectTextColor);
//選中時(shí)搓彻,滑動(dòng)條背景顏色
mSelectBgColor = array.getColor(R.styleable.SlideBar_slb_select_bg_color, mDefaultSelectBgColor);
//未選中時(shí),滑動(dòng)條背景顏色
mUnSelectBgColor = array.getColor(R.styleable.SlideBar_slb_un_select_bg_color, mDefaultUnSelectBgColor);
array.recycle();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setTextSize(sp2px(getContext(), 11f));
if (mTouched) {
mPaint.setColor(mSelectTextColor);
} else {
mPaint.setColor(mUnSelectTextColor);
}
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mTextRect = new Rect();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
//每行的高度嘱朽,計(jì)算平均分每個(gè)字母占的高度
mCellHeight = h / mLetter.length;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec), heightMeasureSpec);
}
/**
* 測(cè)量本身的大小旭贬,這里只是測(cè)量寬度
*
* @param widthMeaSpec 傳入父View的測(cè)量標(biāo)準(zhǔn)
* @return 測(cè)量的寬度
*/
private int measureWidth(int widthMeaSpec) {
/*定義view的寬度*/
int width;
/*獲取當(dāng)前 View的測(cè)量模式*/
int mode = MeasureSpec.getMode(widthMeaSpec);
/*
* 獲取當(dāng)前View的測(cè)量值,這里得到的只是初步的值搪泳,
* 我們還需根據(jù)測(cè)量模式來確定我們期望的大小
* */
int size = MeasureSpec.getSize(widthMeaSpec);
/*
* 如果稀轨,模式為精確模式
* 當(dāng)前View的寬度,就是我們的size
* */
if (mode == MeasureSpec.EXACTLY) {
width = size;
} else {
/*否則的話我們就需要結(jié)合padding的值來確定*/
int desire = size + getPaddingLeft() + getPaddingRight();
if (mode == MeasureSpec.AT_MOST) {
width = Math.min(desire, size);
} else {
width = desire;
}
}
return width;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//觸摸時(shí)改變背景顏色
if (mTouched) {
canvas.drawColor(mSelectBgColor);
mPaint.setColor(mSelectTextColor);
} else {
canvas.drawColor(mUnSelectBgColor);
mPaint.setColor(mUnSelectTextColor);
}
for (int i = 0; i < mLetter.length; i++) {
String text = mLetter[i];
//測(cè)量文字寬高
mPaint.getTextBounds(text, 0, text.length(), mTextRect);
int textWidth = mTextRect.width();
int textHeight = mTextRect.height();
//文字一半的寬度
float textHalfWidth = textWidth / 2.0f;
//字母文字的起點(diǎn)X坐標(biāo)岸军,控件的寬度的一半再減去文字的一半
float x = (mWidth / 2.0f) - textHalfWidth;
//起點(diǎn)文字的Y坐標(biāo)
float y = (mCellHeight / 2.0f + textHeight / 2.0f + mCellHeight * i);
//畫文字
canvas.drawText(mLetter[i], x, y, mPaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
//計(jì)算當(dāng)前觸摸的字母的位置
int index = (int) (y / mCellHeight);
//觸摸
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
mTouched = true;
if (index >= 0 && index < mLetter.length) {
if (mListener != null) {
mListener.onItemSelect(index, mLetter[index]);
}
} else {
return super.onTouchEvent(event);
}
} else {
//松手
mTouched = false;
if (mListener != null) {
mListener.onItemUnSelect();
}
}
//觸摸改變時(shí)奋刽,不斷通知重繪來繪制索引條
invalidate();
return true;
}
public interface OnSelectItemListener {
/**
* 選擇時(shí)回調(diào)
*
* @param position 選中的位置
* @param selectLetter 選中的字母
*/
void onItemSelect(int position, String selectLetter);
/**
* 松手取消選中時(shí)回調(diào)
*/
void onItemUnSelect();
}
public void setOnSelectItemListener(OnSelectItemListener listener) {
mListener = listener;
}
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
public static int px2dp(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
private int sp2px(Context context, float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.getResources().getDisplayMetrics());
}
}