اضافه کردن Indentation به ابتدای محتوای یک سلول
string.Concat(" ",item.Title);
در اینجا مشاهده میکنید که گروه دارایی جاری به همراه حسابهای کل آن در یک سطح نشان داده میشود که این امر خوانایی گزارش را کم میکند.
این گزارش قرار است در 3 سطح کل و معین و تفصیلی گرفته شود.
با تشکر
یازده. در جاوا رویدادها با استفاده از اینترفیسها پیاده سازی میشوند. برای نامگذاری یک رویداد، قاعده آن در جاوا بدین شکل است که نامها به صورت (+ ) Camel نوشته شده و آخرین عبارت هم Listener باشد و نیازی هم به حرف I در نامگذاری اینترفیس نیست؛ چون همه میدانند که این Listener آخری یعنی رویدادی که با اینترفیس پیاده سازی شده است و استفاده از I بی معنی است. هر چند بر خلاف دات نت، در اینجا استفاده از قاعده I چندان متداول نیست.
public interface CopyFileListener { void PublishProgress(long fileSize,long copiedSize); }
دوازده. گوگل اینترفیسهایی را که برای رویدادها میسازد، داخل کلاس اصلی تعریف میکند. پس بهتر هست که شما هم همین روند را ادامه بدید و از این قاعده خارج نشوید. اگر خوب دقت کرده باشید، در برنامه نویسی اندروید تمام اینترفیسها داخل کلاس اصلی هستند:
textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } });
public class MemoryWare { public interface CopyFileListener { void PublishProgress(long fileSize,long copiedSize); } .... }
SetOnClickListener
editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { } });
سیزده. آداپتورها و آداپتور ویوها (چون لیست) قسمت مهمی از برنامههای اندرویدی به شمار میآیند؛ تا حدی که در بیشتر برنامههای ساده هم حضور پررنگی دارند. ولی برای استفاده از این آداپتورها باید بدانید که نحوه کار آنها چگونه است. بسیاری از کاربران در این قسمت اشتباهات زیادی میکنند. اگر در stackOverflow هم در اینباره نگاه کنید، با حجم انبوهی از سوالات روبرو میشوید و فقط به خاطر اینکه نحوه کارکرد آن را نمیدانند، به مشکل برخوردهاند.
کلاس BaseAdapter اصلیترین کلاس آداپتور هاست که بقیه از آن مشتق شدهاند و معروفترین مشتقات آن، کلاسهای CursorAdapter و ArrayAdapter هستند که امکانات بیس آداپتور را افزایش دادهاند.به عنوان مثال در کد پایین از ArrayAdapter استفاده شده است.
نحوه کار یک آداپتور بدین صورت است که متدی را به نام GetView با قابلیت override دارد که با هر تعداد آیتم موجود صدا زده میشود. ولی اگر تصور کنیم فقط چند صدهزار آیتم هم داشته باشیم، آیا واقعا اجرا میشود؟ جواب این سوال این است که با هر بار اسکرولی که شما میکنید آیتمهای بعدی ایجاد میشوند ولی باز این سوال پیش میآید که هر آیتم برای خود جداگانه تشکیل میشود؟ مطمئنا جواب خیر است. آداپتورها از سیستمی به نام ViewRecycler برای کش کردن آیتمهای ایجاد شده استفاده میکنند و با هر اسکرولی که انجام میشود آیتمهای بعدی از روی آیتمهای قبلی که قبلا از صفحه خارج شدهاند، ساخته میشوند و آیتمهای کش شده قبلی را با پارامتری با نام convertView به دست شما میرساند.
کد زیر را ببینید:
@Override public View getView(int position, View rowView, ViewGroup parent) { ViewHolder viewHolder=null; if(rowView==null) { // 1. Create inflater LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); // 2. Get rowView from inflater rowView = inflater.inflate(R.layout.row_bank_group_list, parent, false); viewHolder=new ViewHolder(); viewHolder.txtGroupName=(TextView) rowView.findViewById(R.id.text_groupName); rowView.setTag(viewHolder); } else { viewHolder=(ViewHolder)rowView.getTag(); } viewHolder.txtGroupName.setText(getItem(position).getName()); viewHolder.txtGroupName.setTypeface(new FontSystem().get_General_Font(context)); viewHolder.txtGroupName.setTextColor(context.getResources().getColor(R.color.black)); return rowView; }
کلاس داخلی ViewHolder هم یک الگو برای عدم بررسی Viewهای داخل آن است که نیازی به یافتن و تبدیل مجدد آنها نداشته باشید. در این روش شیء، داخل خصوصیت tag آیتم قرار گرفته است و وقتی از کش برداشته شود، خاصیت تگ آن را میخوانیم و مستقیما مورد استفاده قرار میدهیم. در این حالت شما بهترین استفاده را از پردازشها و حافظه، میکنید.
چهارده. یکی از کارهایی را که قبل از کار کردن در یک مسیر فیزیکی باید انجام دهید این است که مطمئن باشید اجازه نوشتن در آن ناحیه را دارید یا خیر. در غیر اینصورت برنامه شما با خطای FC روبرو میشود و اجرای آن خاتمه مییابد. به همین دلیل اکثر برنامه نویسان از متد CanWrite در کلاس File استفاده میکنند. ولی در هنگام استفاده از این متد باید دقت داشته باشید که کلاس File فقط باید حاوی مسیر باشد و اسمی از فایل مربوطه در آن نباشد. دلیل هم آن است که این احتمال میرود اگر فایلی هم وجود نداشته باشد، مقدار false را به شما برگرداند. مثال زیر قرار است فایلی را در کارت حافظه بنویسید، ولی بررسی اجازه نوشتن در مسیر، اشتباه است:
File file=new File(sdcardPath,fileName); if(file.CanWrite()) { ..... }
File file=new File(sdcardPath); if(file.CanWrite()) { file=new File(sdcardPath,filePath); ..... }
پانزده. کارت حافظه خارجی: همه برنامه نویسان اندروید حداقل یکبار از کد زیر استفاده کرده اند:
Environment.getExternalStorageDirectory()
هر برنامهای که در اندروید نصب میشود در مسیر
/Data/Data
/Data/Data/Info.Dotnettips.MyApp
/** * it will returns sd path for you * <p> * <b>Required Permission: </b>android.permission.READ_EXTERNAL_STORAGE<br/> * </p> * @return */ public List<String> GetExternalMounts() { final List<String> out = new ArrayList<>(); String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*"; String s = ""; try { final Process process = new ProcessBuilder().command("mount") .redirectErrorStream(true).start(); process.waitFor(); final InputStream is = process.getInputStream(); final byte[] buffer = new byte[1024]; while (is.read(buffer) != -1) { s = s + new String(buffer); } is.close(); } catch (final Exception e) { e.printStackTrace(); } // parse output final String[] lines = s.split("\n"); for (String line : lines) { if (!line.toLowerCase(Locale.US).contains("asec")) { if (line.matches(reg)) { String[] parts = line.split(" "); for (String part : parts) { if (part.startsWith("/")) if (!part.toLowerCase(Locale.US).contains("vold")) if(new File(part).canWrite()) out.add(part); } } } } return out; }
شانزده. یکی از روشهای انتقال اطلاعات بین اکتیویتیها مختلف استفاده از Extras هاست که شما با تعیین یک نام یا کلید، اطلاعات مربوطه را ارسال و توسط همان کلید؛ اطلاعات را در اکتیویتی مقصد دریافت میکنید:
notesIntent.putExtra("PartyId", PartyId); startActivity(notesIntent);
PartyId=getIntent().getLongExtra("PartyId",0);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false); i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false); i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_FILE);
هفده. قواعد نامگذاری: برای نامگذاری متغیرها از قانون CamelCase استفاده میکنیم. ولی برای حالات زیر از روشهای دیگر استفاده میشود:
- برای ثابتها از حروف بزرگ و _ استفاده کنید.
- برای متغیرهای خصوصی از حرف m در ابتدای نام متغیر استفاده کنید.
- برای متغیرهای استاتیک از حرف s در ابتدای نام متغیر استفاده کنید.
public class MyClass { public static final int SOME_CONSTANT = 42; public int publicField; private static MyClass sSingleton; int mPackagePrivate; private int mPrivate; protected int mProtected; }
هجده: قاعده نظم و ترتیب در importها توسط مستندات گوگل بدین شکل تعریف شده است:
- نام پکیجهای ارائه شده توسط گوگل
- نام پکیجهای ثالث
- نام پکیجهای موجود در java و javax
- پکیجهای موجود در پکیج اصلی
نوزدهم. مرتب سازی متدهای دسترسی یک کلاس: بسیار خوب است که همیشه کدهای ما نظم خاصی را داشته باشد تا پیگیریهای شخصی و تیمی در آن راحتتر صورت بگیرد. برای مثال در یک کلاس ابتدا متدهای public و سپس private قرار گیرند و الی آخر.
الگوی عمومی که برای کار با جاوا صورت گرفته است به شکل زیر میباشد:
public, protected, private,abstract, static, transient, volatile, synchronized, final, native.
ادیتور intelij شامل تنظمیاتی برای مرتب سازی کدهاست که در این مورد بسیار سودمند است. با طی کردن مسیر زیر میتوانید برای آن ترتیب اینگونه موارد را مشخص کنید.
Settings>Editor>Code Style>Arrangement
در تصویر بالا متدها به ترتیب متدهای دستری بین بلوکهای کامنت method start و method end قرار گرفته اند.
همچنین شامل گزینههای دیگری نیز میباشد که به نظرم فعال کردنشان بسیار خوب است. گزینه keep overridden methods together به شما کمک میکند تا متدهایی را که رونویسی میشوند، در کنار یکدیگر قرار بگیرند که برای کلاسهای اندرویدی مثل اکتیویتیها و فرگمنتها و ... بسیار خوب است. گزینه مفید دیگر Keep dependent methods together است که در دو حالت عمقی یا خطی متدهای وابسته (متدهایی که متدهای دیگر را در آن کلاس صدا میزنند) در کنار یکدیگر قرار میدهد و مابقی گزینهها، که بسیار سودمند هست. به هر حال هر قاعدهای را که برای خود انتخاب میکنید اگر در حالت پیش فرض نیست بهتر است در مستندات پروژه ذکر شود تا افراد دیگر سریعتر به موضوع پی ببرند.
قسمت بیستم. این مورد برای افراد تازه کار میباشد که تازه اندروید استادیو را باز کردهاند و مشغول کدنویسی میباشند. یکی از مواردی که در همان مرحله اول به آن برمیخورید این است که intellisense ادیتور به بزرگی و کوچکی حروف حساس است و تنها با حرف اول سازگاری دارد. برای تغییر این مسئله باید مسیر زیر را طی کنید:
Settings>Editor>Completion>Case-sensitive Completion>None
ما میتوانیم بعد از ساخت جدول و انتشار مقداری داده در آن قیدهایی را ایجاد کنیم. بطور پیشفرض اگر شرط قید ما بر قرار بود قید به طور صحیح ساخته میشود و اگر شرط قید ما بر قرار نباشد قید با خطای conflict مواجه خواهد شد.
بطور کلی غیر فعال کردن قیدها کار درستی نیست. ولی در برخی مواقع برای تسریع در اجرای کد میتوانیم قید را غیر فعال کنیم. بطور مثال اگر یک میلیون داده قرار است در جدول درج شود و مطمئن هستیم که این دادهها جامعیت دادهها را حفظ میکنند آنگاه میتوانیم قید را برای تسریع در عمل درج بطور موفق غیر فعال کنیم.
فعال و غیر فعال کردن از طریق DDL
با غیر فعال کردن قیود دادهها را در وضعیت نامناسبی قرار میدهیم ولی همان طور که اشاره شد بطور موفق اشکالی پیش نخواهد آمد.
در ادامه ابتدا طریقه غیرفعال کردن و مجددا فعال کردن قیود را توسط دستور alter table نشان خواهم داد سپس به سراغ امکانات ویزاردی میرویم. ابتدا یک جدول تک ستونه ایجاد میکنیم:
CREATE TABLE testTable (column1 integer not null);
الان هیچ قیدی روی جدول لحاظ نشده است. پس هر داده که در رنج domain ستون باشد را میتوانیم درج کنیم. پس بطور نمونه این دادهها را درج میکنیم:
INSERT INTO testTable VALUES (-10), (0), (10), (20), (30), (40)
حالا تصمیم داریم قیدی روی ستون column1 بگذاریم که توسط آن تنها اعداد مثبت در جدول درج شوند. پس داریم:
ALTER TABLE testTable WITH CHECK ADD CONSTRAINT NoNegative CHECK (column1 > 0);
The ALTER TABLE statement conflicted with the CHECK constraint "NoNegative".
برای ساخت این قید روی این دادهها تنها راه استفاده از کلید واژههای WITH NOCHECK است یعنی:
ALTER TABLE testTable WITH NOCHECK ADD CONSTRAINT NoNegative CHECK (column1 > 0);
و اکنون سعی میکنیم یک مقدار منفی در جدول درج کنیم:
INSERT INTO testTable VALUES (-5) /* The INSERT statement conflicted with the CHECK constraint "NoNegative". */
اما قیدی که ساخته بودیم در جدول در حال اعمال شدن است. برای درج مقدار منفی باید غیر را غیر فعال کنیم.
ALTER TABLE TestTable NOCHECK CONSTRAINT NoNegative
و حالا مقدار منفی را درج میکنیم. و برای برگرداندن وضعیت NOCHECK به وضعیت CHECK باید از کلید واژههای WITH NOCHECK استفاده کنیم. چرا که داده هایی در جدول درج شده اند که قید مورد نظر ما را نقض میکنند.
ALTER TABLE TestTable WITH NOCHECK CHECK CONSTRAINT NoNegative
فعال و غیر فعال کردن از طریق design
در قسمت object explorer قید مورد نظر را پیدا کرده و روی آن راست کلیک کرده و گزینه Modify را انتخاب کنید. سپس در پنجره باز شده در قسمت Table Designer تغییرات مورد نظر خود را اعمال کنید.
شاید در ابتدا فراخوانی متدی از یک کنترلر در یک View کار سختی به نظر برسد، ولی در واقع با استفاده از مفاهیم Lambda expressions و Delegateها این کار بسیار راحت خواهد بود.
برای این کار میتوانیم متد مورد نظر را به صورت یک delegate تعریف کرده و به view ارسال کنیم. فرض کنیم متدی داریم برای برگرداندن مجموع 2 عدد به صورت string:
public string Sum(int a,int b) { return (a + b).ToString(); }
حال برای اینکه بتوانیم این متد را بصورت یک delegate به view ارسال کنیم لازم است تا یک delegate را بصورت public و در خارج از تعریف کلاسها و درون یک namespace مشخصی تعریف کنیم. در اینجا برای راحتی در همان MvcTest.Controllers namespace یک delegate را بصورت زیر (MvcTest نام پروژه است) تعریف میکنیم:
public delegate string SumOf2Number(int a, int b);
حال میتوانیم بصورت زیر این متد را از طریق ViewBag به View ارسال کنیم:
SumOf2Number sum2numbers = Sum; ViewBag.SumFunc3 = sum2numbers;
در روش دوم، میتوانیم متد مورد نظر را بصورت Func به View ارسال کنیم. این کار را میتوانیم به دو صورت انجام دهیم، که هر دو را در تکه کد زیر خواهید دید:
ViewBag.SumFunc =(Func<int,int,string>) Sum;//way 1 ViewBag.SumFunc2 = (Func<int, int, string>)((int a, int b) => { return (a + b).ToString(); });//way 2
میتوانیم یک Lambda expression را به یک متغیر delegate نیز ربط دهیم؛ به این صورت:
SumOf2Number sum2numbers2 = (int a, int b) => { return (a + b).ToString(); };
در نهایت کد بخش کنترلر کلاً به اینصورت خواهد بود:
namespace MvcTest.Controllers { public delegate string SumOf2Number(int a, int b); public class HomeController : Controller { public ActionResult Index() { SumOf2Number sum2numbers = Sum; SumOf2Number sum2numbers2 = (int a, int b) => { return (a + b).ToString(); }; ViewBag.SumFunc =(Func<int,int,string>) Sum; ViewBag.SumFunc2 = (Func<int, int, string>)((int a, int b) => { return (a + b).ToString(); }); ViewBag.SumFunc3 = sum2numbers; ViewBag.SumFunc4 = sum2numbers2; return View(); } public string Sum(int a,int b) { return (a + b).ToString(); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } } }
و در Index View خواهیم داشت: (البته اصولاً استفاده از controller namespace در سمت view کار درستی نیست، منتها اینجا فقط یک مثال کاربردی ساده است)
@using MvcTest.Controllers; @{ ViewBag.Title = "Home Page"; } <h1> @ViewBag.SumFunc(7,8) </h1> <h1> @ViewBag.SumFunc2(9, 10) </h1> <h1> @ViewBag.SumFunc3(5, 1) </h1> <h1> @ViewBag.SumFunc4(2, 3) </h1>
پردازش فایلهای XML با استفاده از jQuery
ایجاد چارت سازمانی تحت وب #2
آموزش Prism #2
EF Code First #12
در برنامههای وب، کار صرفا به یک کلیک ساده ختم نمیشود که در همان لحظه، یک Context آغاز و پایان یابد. در طی یک درخواست وب، قسمتی از صفحه لیست گروهها، قسمتی دیگر لیست نویسندگان، قسمتی دیگر گزارش درصد استفاده از مرورگرها و قسمتی دیگر لیست آخرین مطالب را نمایش میدهد. تمام اینها هم در طی یک درخواست رخ میدهند و هرکدام، ماژول ماژول طراحی شدهاند و از هم جدا.
اینجا است که ارزش استفاده از قابلیتهای مدیریت طول عمر IoC containers برای به اشتراک گذاری یک DbContext در طی یک درخواست بهتر مشخص میشود. به این ترتیب میشود به سرباری کم و سرعتی بالا دست یافت چون مدام به ازای قسمتهای مختلف برنامه Context ایجاد و تخریب نمیشود.